Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I able to re-assign to a moved variable?

I found this code compiles:

let mut s1 = String::from("hello");
let _s2 = s1;
s1 = String::from("world");
println!("{}", s1);

If the third line is not there, I will get an error complaining that a moved variable cannot be borrowed, which is understandable to me:

error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:5:16
  |
2 | let mut s1 = String::from("hello");
  |     ------ move occurs because `s1` has type `std::string::String`, which does not implement the `Copy` trait
3 | let _s2 = s1;
  |           -- value moved here
4 | //s1 = String::from("world");
5 | println!("{}", s1);
  |                ^^ value borrowed here after move

With the third line, the code compiles.

Here's my understanding: after s1 is moved, s1 is still in some kind of valid state, it's just the compiler that prevents you from accessing it; if you re-assign it, it gets new ownership, and the compiler understands it so it compiles.

Am I right? Where can I find any doc/spec that explains this officially?

like image 645
taotsi Avatar asked Oct 19 '25 05:10

taotsi


1 Answers

The original value of s1 is moved, so it is illegal to try to use s1 - right up until the point that it is given a new value.

Here's my understanding: after s1 is moved, s1 is still in some kind of valid state, it's just the compiler that prevents you from accessing it; if you re-assign it, it gets new ownership, and the compiler understands it so it compiles.

It isn't valid. When a s1 is moved, the binding is now to uninitialized memory, so it is illegal to use it at all. Giving it a new value means that the memory is no longer uninitialized and it is safe to use again.

Compare with the following:

let mut s1; 
s1 = String::from("world");
println!("{}", s1);

On the first line, s1 is in exactly the same state as after being moved in your original code.

Note that the compiler may choose to use a different memory address for the second value. References to the original value are not allowed to live past the move, so this implementation detail is completely unobservable unless you start printing out raw pointers.

You might infer from this that reassigning to a moved mutable binding is semantically equivalent to introducing a new binding that shadows the first. This is almost true, but not quite. The order in which bindings are dropped is opposite to that in which they were introduced, and the drop order here reflects the order of the original bindings.

like image 196
Peter Hall Avatar answered Oct 22 '25 05:10

Peter Hall



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!