Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens when you use multiple nested scopes?

Tags:

rust

fn main() {
    let mut m = 12;
    {
        let n = &mut m;
        *n = 13;
        {
            let k = n;
            *k = 20;
            println!("{}", k);
        } // k's scope ends here, right?
        println!("{}", n);
    }
    println!("{}", m);
}

Here is what I got when I ran the code:

src/main.rs:11:18: 11:19 error: use of moved value: `n` [E0382]
src/main.rs:11      println!("{}", n);
                               ^

But hasn't the variable k ended its scope yet? Why wasn't the ownership given back to variable n?

like image 951
user3099695 Avatar asked Feb 09 '23 15:02

user3099695


1 Answers

But isn't the variable k has ends its scope yet? Why didn't it give the ownership back to variable n?

Yes, k's scope has ended, but why do you think that it should give ownership back?

In Rust nothing can "give ownership back". If a type does not implement Copy (and &mut references absolutely do not), then its values can only be moved. Move implies ownership transfer, so it is up to the receiver of the ownership to decide what to do with the value. So, when k goes out of scope, the pointer is effectively "destroyed" (the pointer itself, not the value). Since it was moved out of n, the binding effectively became uninitialized, so you get this error.

&mut references are unique in that, while they are not copyable and therefore can only be moved, sometimes they are automatically reborrowed, that is, the compiler automatically inserts &mut *p for you. I don't remember exact rules when automatic reborrowing is applied (as far as I remember, this happens when mutable reference is passed to functions and probably somewhere else), but this is not such situation. To make your code work you'd want to reborrow the value explicitly:

fn main() {
    let mut m = 12;
    {
        let n = &mut m;
        *n = 13;
        {
            let k = &mut *n;  // explicit referencing of the dereferenced value
            *k = 20;
            println!("{}", k);
        }
        println!("{}", n);
     }
     println!("{}", m);
}

This way the compiler knows that n is not moved into k and allows you to use it after k's scope ends.

Just as a side note, the following (thanks Veedrac for reminding) also works, again, due to automatic reborrowing:

fn main() {
    let mut m = 12;
    {
        let n = &mut m;
        *n = 13;
        {
            let k: &mut _ = n;  // automatic reborrowing because of type annotation
            *k = 20;
            println!("{}", k);
        }
        println!("{}", n);
     }
     println!("{}", m);
}

It appears that if the compiler knows that the target type is a mutable reference, it will reborrow the original reference, otherwise, that is, if the target type is unknown (e.g. in generic context or when assigning to a binding without explicit type annotation), the reference is moved, not reborrowed. So yes, behavior around mutable references may be confusing somewhat.

like image 99
Vladimir Matveev Avatar answered Feb 19 '23 09:02

Vladimir Matveev