'Rust borrowing - why does it work and not ownership? [duplicate]

I understand why Rust doesn't allow us to do the following:

let v1 = vec![1,2,3];
let v2 = v1;
println!("Print here {}", v1[0])

But now, let's look at this, which is allowed and correct.

let v1 = vec![1,2,3];
let v2 = &v1; // borrowing here
println!("Print here {}", v1[0])

Now, I am really curious why 2nd one is allowed. For the first example, on the stack, some information gets stored(pointer of the memory address of the actual vector data, length of the vector). let v2 = v1 causes pointer and length of data to be copied from that part of the stack and gets stored on the stack again, but now, for v2 variable. Since changing v1 causes vector data to be changed + the part of the stack where v1's information was stored, rust doesn't allow this, because v2 now becomes unpredictable as it still stores the old length so stack and heap doesn't agree.

For the 2nd example, I am wondering what let v2 = &v1 does exactly so that this is allowed? I'd appreciate the explanation in the same words as I described the first case above.



Solution 1:[1]

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 anymore 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 anyways, 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 droped). 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 anymore, so it can be droped 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]);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 BlackBeans