There can only ever be one owner of the value, otherwise the compiler can't do its magic to guarantee that all references are dropped before the owned value. The owner is responsible for freeing the memory when it's done, so having two owned values of the same thing would indeed result in a double-free. Therefore, you can never upgrade a reference to an owned value. If you ever need to own the value, you need to either copy it, or own it by moving it. Or live with just a reference to it, which you're only permitted to have as long as the owned value is live. That's the lifetime concept that gets tracked.
In your second example, it only works because the compiler knows you're done with the reference, so you can move the value. If you try to print s_ref
in your second example after you moved s
into s2
, it wouldn't compile because the reference has been invalidated by the move. After the move, s2
could be in a different memory address than s
was, so s_ref
points to memory that could have been reused for something else. You can't have that, that's a memory bug, and Rust doesn't allow you to do that, it's what it's designed to do.
Yes let s2 = *&s
makes sense logically, but in practice you're still going owned->reference->owned without moving ownership of the original s
, so it's not permitted. The example is trivial to optimize out, but what if that reference gets passed down a whole bunch of functions, potentially in a dynamic library the compiler has no visibility into at compile time? It can't possibly track than and guarantee memory safety. But if you only pass references to the dynamic library, it knows by the time the function call is done and you're out of the library, nobody's using the value anymore and is therefore safe to drop. The lifetime annotation (implicit or explicit) guarantees that the called function is not allowed to keep the reference around, because it's only borrowed and your main function wants its value back.
The typical workaround for this is an Rc or Arc. Those add extra logic so that the value is only dropped once, when all owners have dropped the Rc, then the Rc knows it can safely drop the inner value as well.
With Rust, always think in terms of "who owns this value". There's only one owner, and one or many borrowers. Like physical items, when it's borrowed, you don't technically have it even though you own it. You have a reasonable expectation that the borrower will return the item back to you, and only then you're free to change it again. Otherwise it could result in the called function to see a surprise change of the value while it has an immutable reference to it!