When reasoning about what Rust allows or does not allow, in term of ownership, it's not useful to think about what happens exactly in memory, because that's not how Rust thinks about it. It has a more high-level approach.
The fundamental issue with the first code is that you have emptied the variable v1
from having any value, when assigning its value to v2
. That is, each value (part of memory) in Rust must have an owner (that is, a variable that has a lifetime, and which is responsible for that value), and only one owner.
When you move v1
's value to v2
, nothing happens in memory (it' most likely a NOP with sufficient optimization levels), but in the Great Scheme of Rust, the value's owner has changed. But then, to ensure there is only one owner, Rust makes it like v1
's value moved. It's a way to tell you: it's just like there if there was nothing any more at the address of this variable, stop using it until you have put something else yourself, because I won't allow you to access what is really there until then. And since v1
was declared non-mutable anyway, this variable is pretty much useless from now on.
In the second case, the situation is completely different. The ownership of the underlying value is still v1
's. v2
has a different type of access to the value: it is not responsible for it, has no write-access, and must give it back before the value is freed from memory (that is, before v1
is drop
ed). However, for the lifetime of v2
(that is, for as much time as v2
could be used somewhere), the value is "locked" (this has nothing to do with threading locks), in the sense that you are not allowed to write to it, to share it with write-access or to give its ownership to somebody else. If you wanted to share the data with write-access, you don't want to give the ownership (presumably because you want to reuse the variable after the borrow has ended, and getting the ownership back is verbose), then you can give a mutable borrow. These are exclusive (meaning at most one other person can have a mutable borrow) and prevent the actual owner from reading or writing while the borrow is on-going.
It would be achieved like this:
let mut owner = vec![1, 2, 3];
let borrower = &mut owner;
println!("Print here {}", owner[0]);
However, if you compiled this code, you'll see it works. Strange, isn't it? I just said even the owner has no read-access to the value... The key is that the lifetime of the borrow ends just before the owner tries to read-access it, that is, Rust understands that, when trying to access the Vec
, borrower
will never use its borrow any more, so it can be dropped just before the access. The following code will not compile, because in this case it cannot drop
borrower
before owner
tried to access the borrowed value.
let mut owner = vec![1, 2, 3];
let borrower = &mut owner;
println!("Print here: {}", owner[0]);
println!("Print there: {}", borrower[0]);