What happens on the stack when one value shadows another in Rust? [duplicate]

这一生的挚爱 提交于 2020-02-03 10:36:50

问题


I am reading Mastering Rust. There is an exercise at the end of the first chapter where sample code is provided, and the task is to fix it, iterating by using the generally quite helpful compiler error messages.

I was expecting that the following was an error but it is not:

for line in reader.lines() {
    let line = line.expect("Could not read line.");

For complete context, I have the entire code in a gist. That's the code after I fixed things, and the relevant rows are 37 & 38. However it requires feeding a text file as an argument.


I was expecting an error because line is on the stack (at least the pointer is). Is it right that it can still be destroyed and replaced with no complaint?

What happens under the hood regarding memory management and the stack? I presume that line is actually a reference to a string (a &str type). So, then, this is fine because in either case, the pointer itself - the object on the stack - is just a usize, so that both line objects are of the same size on the stack.

Can I do this with something of a different size? Could the second line have said:

let line: f64 = 3.42;

In this case, the object itself is on the stack, and it is potentially larger than usize.


回答1:


Whenever a variable is declared with let, it's an entirely new variable, separate from anything before it. Even if a variable with the same name already exists, the original variable is shadowed while the new variable is in scope. If a variable is shadowed, it's normally inaccessible.

It's possible to access the value of the old variable in situations where the old variable is still in scope after the new variable falls out of scope, or if the old variable has a Drop implementation.

We can see this in action in the following example.

#[derive(Debug)]
struct DroppedU32(u32);

impl Drop for DroppedU32 {
    fn drop(&mut self) {
        eprintln!("Dropping u32: {}", self.0);
    }
}

fn main() {
    let x = 5;
    dbg!(&x); // the original value
    {
        let x = 7;
        dbg!(&x); // the new value
    }
    dbg!(&x); // the original value again

    let y = DroppedU32(5);
    dbg!(&y); // the original value
    let y = DroppedU32(7);
    dbg!(&y); // the new value

    // down here, when the variables are dropped in
    // reverse order of declaration,
    // the original value is accessed again in the `Drop` impl.
}

(playground)

That's not to say that the original variable is guaranteed to still exist. Compiler optimizations can cause the original variable to be overwritten, especially if the original variable isn't accessed again.

The code

pub fn add_three(x: u32, y: u32, z: u32) -> u32 {
    let x = x + y;
    let x = x + z;
    x
}

compiles to

example::add_three:
        lea     eax, [rdi + rsi]
        add     eax, edx
        ret

If you're like me and aren't too familiar with assembler code, this basically

  1. Adds x and y and puts the result in a variable (call it w).
  2. Adds z to w and overwrites w with the result.
  3. Returns w.

So (besides the input parameters), there's only one variable used, even though we used let x = ... twice. The intermediate result let x = x + y; gets overwritten.



来源:https://stackoverflow.com/questions/56370397/what-happens-on-the-stack-when-one-value-shadows-another-in-rust

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!