« use of moved value » when using a reference to a vector

Hello,

I am scratching my head with this error. I have a mutable reference to a vector, which I want to loop over multiple times, but I cannot do it because my reference seems to itself be moved when I loop over it the first time.
With the following (simplified) code :

fn main() {
    let mut values = vec![10, 11, 12];
    let v = &mut values;

    let mut max = 0;
    
    //for n in &mut values {
    for n in v {
        max = std::cmp::max(max, *n);
    }

    println!("max is {}", max);
    println!("Converting to percentages of maximum value...");
    //for n in &mut values {
    for n in v {
        *n = 100 * (*n) / max;
    }
    println!("values: {:#?}", values);
}

I get the following error :

error[E0382]: use of moved value: `v`
   --> src/main.rs:15:14
    |
3   |     let v = &mut values;
    |         - move occurs because `v` has type `&mut Vec<i32>`, which does not implement the `Copy` trait
...
8   |     for n in v {
    |              -
    |              |
    |              `v` moved due to this implicit call to `.into_iter()`
    |              help: consider borrowing to avoid moving into the for loop: `&v`
...
15  |     for n in v {
    |              ^ value used here after move
    |

Now if I follow the advice of the compiler and replace v with &v in the first for, I get this new error :

error[E0277]: `&&mut Vec<{integer}>` is not an iterator
 --> src/main.rs:8:14
  |
8 |     for n in &v {
  |              ^^ `&&mut Vec<{integer}>` is not an iterator
  |
  = help: the trait `Iterator` is not implemented for `&&mut Vec<{integer}>`
  = note: required by `into_iter`

Also, if I replace both for n in v by the (currently commented out) for n in &mut values, everything works as I expect. :thinking:
(But I cannot do that because in my real world code I only have the mutable reference v.)
Could someone explain to me what is happening here ? It looks like the v reference is itself moved on the first for and not « inside » v anymore, and that does not happen when creating the reference within the for n in &mut values (presumably because it then creates two new references ?), but I am not sure I understand it fully.
What is the correct way to solve that ? And what exactly is happening ?
Thank you.

A mutable reference is unique, so it can indeed not be copied, and the for loop will consume it. However, it is possible to reborrow a mutable reference, which creates a sub-mutable reference to the same thing. While the reborrow exists, the original is not usable to enforce the uniqueness.

To reborrow, use &mut *the_mut_ref.

for n in &mut *v {
    ...
}

Note that reborrows happens automatically whenever you use a mutable reference in a place that can only accept a mutable reference, but for loops can accept many types, including non-mutable references, so it doesn't happen here.

6 Likes

Ugh, that's a pretty bad suggestion

3 Likes

Thank you for your answer. :pray: It works.
Does it incur any additional overhead, like dereferencing a pointer and then re-creating a new one ?

Indeed, it is misleading and did not help me understand the problem. Maybe the compiler could say something about reborrowing when trying to loop over a reference ?

No, explicitly reborrowing doesn't incur any overhead, and even if it did, the overhead would be optimized out by the compiler.

You can submit bad errors messages as bugs to rustc.

3 Likes

My approach to needing to reborrow in this situation is to overwrite the previous alias. I consider this approach to be consistent with the idiomatic reuse of an alias name for repeated calls to functions that return some interim, “work in process”, value (anyone disagree?).

This version of the code compiles by simply repeating let v = &mut values; .

This is done (here). Sorry for the delay.
Thank you again for your help. :pray:

No worries, everything less than a week is still pretty fast.

Since I haven’t seen it mentioned in this thread yet and it might be syntactically nicer: You can (at least for the case of &mut Vec<T>) just call the iter_mut method, as in for n in v.iter_mut().

For some reason that I have yet to fully wrap my head around, even writing for n in v.into_iter() makes the code compile. I’m personally of the opinion that the compiler really should be changed to insert implicit re-borrows in way more places anyways so that your original code would compile as well.

1 Like

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.