Assigning initialized vector to reference

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`

Thanks!

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?).

3 Likes
fn function(seq: &Vec<u16>) -> Option<u16> {
....
}

function(&vec![1,2,3]);

Why is the above legal then?

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.

1 Like

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, reference is 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.

âźą The book on: the Copy trait.

6 Likes

TL;DR, the compiler already had the right fix for you here:

 help: consider removing the `&mut`: `length`

which suggests changing

let &mut length = &mut vec![1, 5];

into

let length = &mut vec![1, 5];
1 Like

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.


fn function(seq: &Vec<u16>) -> Option<u16> {
....
}

function(&vec![1,2,3]);

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.

fn f(vec: &mut Vec<i32>) -> &mut i32 {
    &mut vec[0]
}

fn foo() {
    let r = f(&mut vec![1]);
    println!("{:?}", r); // error
}

(well, more precisely, the let still works, but using r 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
}
5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.