Why move through reference is restricted in below scenario

I don't see any difference between line no 3 & 6 in both the lines I'm trying to transfer ownership but why line no 6 fails in this case.

    struct Foo{}
    let a = Foo{};
    let b = a; // Move occurs from a to b. this is valid as per rust rules.

    let c = Foo{};
    let d = *&c; // looks like here too move occurs but through a reference . when clearly I'm not using `c` 
//anywhere further in the code why this line tends to fail. according to me this should have passed.

In general, one of the primary properties of a reference is that it offers access to the referent while guaranteeing that the original value borrowed from is still valid afterward, regardless of what the user of the reference does. And & references in particular are copiable, so moving out of one would have to invalidate the others.

In this specific case, we could imagine the compiler noticing that &c is a borrow of local variable and therefore there's no harm in letting the function do what it likes with that particular value, invalidating c. But, that would then be a special rule for references to local values. It would complicate the compiler, and complicate explaining the language, without much practical usefulness.

Note that the program would compile if Foo implemented Copy — it's valid to copy out of an & reference (and this is done all the time for types like integers).

5 Likes

You're trying to treat references as pointers, or what other languages mean by "access by reference". Rust's references are a different thing. They're not as general-purpose.

Rust's references are loans. They're more like compile-time read/write locks. They're not supposed to give you an object like Box does. They are only temporarily letting you to view an object (or more accurately, a place) that you can't keep using and aren't allowed to invalidate. This subtle difference is the entire purpose of references.

So the answer is a bit tautological: move through reference is restricted, because Rust's references are a tool for restricting moves.

Having said that, Rust has some intentional loopholes. Truly unmovable objects don't exist in Rust, and a combination of &mut + std::mem::swap lets you actually move an object from behind a reference by swapping it with another valid object. And there's interior mutability which allows doing the same even behind a shared & reference.

4 Likes

There is a very concrete technical and logical reason why this shouldn't work. It's not just a language definitional problem.

Although you are not using c, it's still dropped at the end of the scope. If it were moved from, that would cause a use-after-free. (Even though your type here doesn't free anything, consider that this sample could be rewritten with e.g. Vec or String just as well.)

Even though here you only have a single reference, in general you can have many immutable references, and they don't need to be inside the same function (or even module or crate) as the value they are referencing. So the compiler can't possibly hope to discover and prove that moving out of a single particular reference (that happens to be unaliased) is fine. It has to assume conservatively that it isn't.