Can someone help me understand why I can't do something like the following?
let &mut length = &mut vec![1, 5];
I end up getting the following error
4 | let &mut length = &mut vec![1, 5];
| ----------- ^^^^^^^^^^^^^^^
| | |
| | data moved here
| | move occurs because `length` has type `Vec<u16>`
, which does not implement the `Copy` trait
| help: consider removing the `&mut`: `length`
In Rust, references are not allowed to outlive the thing that they're referencing, which must be kept alive by other means. The vector here is a temporary value that is cleaned up at the end of the statement, but the length variable stays around after the statement ends.
Actually, the error message is about not being able to move out of the reference.
Simply put, the pattern match of the LHS implies that the length binding should be bound to the value of the vector. However, the vector is behind a reference, and you are not allowed to move out of a reference for obvious reasons (what would it point to, then, if the pointee were moved from?).
Because there you are passing the reference itself, rather than moving what is behind the reference. Changing it to pattern match against the passed in vector (as in this playground), does fail (with a similar error to your original post), since then you would be trying to take ownership of what is behind the reference.
This needs some more explanation, otherwise you wouldn’t ask
First, this is how you can “fix” your code:
let length = &mut vec![1, 5];
now, what does your extra &mut do to make the compiler unhappy? Here’s an example that does compile but uses i32 instead of a vector:
let &mut x = &mut 42;
Crucially now, x is not a reference, no. x is just an i32.
The example
let &mut x = &mut 42;
is equivalent to
let reference = &mut 42;
let &mut x = reference;
Here, referenceis a reference. But x isn’t. The assignment
let &mut x = reference;
is actually the same as
let x = *reference;
or with a type:
let x: i32 = *reference;
The &mut is a pattern (as in pattern-matching) that dereferences the reference.
It’s analogous to
let (a, b) = (1, 2); // similar to: let &mut x = &mut 42;
where a and b are not tuples but i32s. Equivalently in two steps
let pair = (1, 2); // similar to: let reference = &mut 42;
let (a, b) = pair; // similar to: let &mut x = reference;
And then
let (a, b) = pair; // similar to: let &mut x = reference;
could be rewritten as
let a = pair.0; // together with the next line
let b = pair.1; // similar to: let x = *reference;
Now back to Vec, your’re basically trying to do
let reference: &mut Vec<i32> = &mut vec![1, 5];
let length: Vec<i32> = *reference;
The second step doesn’t work. The value length is supposed to be owned, but you cannot obtain ownership of a Vec by merely dereferencing the reference to the Vec. If you want to own it, you’d need to, for example, create your own clone (but cloning has extra overhead, a new allocation, etc. something you won’t want to do by accident, so a .clone() call would need to be explicit). The reason why this isn’t a problem with i32 is because of the concept of “copy” types and the Copy marker trait that allows implicit copies for some types where all that’s needed is a “shallow, bitwise” copy of the data to get your own owned copy of the value.
Thank you so much! This clarified my gap in understanding. I wasn't aware that there was pattern matching happening here. This helped a lot. One thing I'm still unclear on is; does there need to be an owner for each object? In the example below, I'm confused who'd be the owner here or what the lifetime of the vector would be since I reference the vector once it gets created.
The vector in this case lives in a temporary variable. Or short, called a “temporary”. This is the same kind of concept that you’ll also have for e.g. intermediate results if you chain some functions à la f(g(x)), or x.foo().bar() [if foo and bar take self, not &self or &mut self]. The intermediate result f(x) or x.foo() lives in a temporary location, this temporary variable owns the value then. These generally are like short-lived local variables in the containing scope (i.e. the containing block), they live until the end of the containing statement and get dropped then, but the rules are a bit complicated on what exactly the “containing statement” is.
There’s also a special case of “temporary lifetime extension” which is the very reason why writing let length = &mut vec![1, 5]; (and working with length afterwards) works in the first place. In this case, the temporary lives until the end of the containing scope (so, again, the containing block usually; you’d consider the body of a function a block, too, for this kind of analysis.) To give a counterexample where temporary lifetime extension won’t happen, if you have a function fn f(&mut Vec<i32>) -> &mut i32 then something like let r = f(&mut vec![1])doesn’t work anymore. It’s an entirely syntactical criterion when temporary lifetime extension happens, no deep reasoning behind it other than that it works the way it’s defined to work by the language specification and that it’s often useful the way it is.
(well, more precisely, the let still works, but usingr later doesn’t work, because the temporary is dropped after the let statement, thus invalidating the reference r.)
Applying this to your example function(&vec![1,2,3]); for example in a function
fn foo() {
// some code
let value = function(&vec![1,2,3]);
// some more code
}
this is essentially the same as writing
fn foo() {
// some code
let value = { // <- opening a block, sumulating the temporary scope
// limited to the single statement
let tmp = vec![1,2,3];
let tmp_ref = &tmp;
let result = function(tmp_ref);
result // <- return value of the block
// will be assigned to the variable `value`
}; // variables `tmp_ref` and `tmp` dropped at end of block
// some more code
}