ban
ban

Reputation: 155

Mutable reference to a vector was moved due to this implicit call to `.into_iter()`, but calling `.into_iter()` explicitly works

This will fail:

fn ppv(arr: &mut Vec<i32>) {
    if arr.len() <= 0 {
       return;
    }
    let mut pp: i32 = 0;
    for &mut val in arr {
        if val == pp {
            pp = val;
        }
    }
    println!("arr is {:?}", &arr);
}

But this will pass:

fn ppv(arr: &mut Vec<i32>) {
    if arr.len() <= 0{
       return;
    }
    let mut pp: i32 = 0;
    for &mut val in arr.into_iter() {
        if val == pp {
            pp = val;
        }
    }
    println!("arr is {:?}", &arr);
}

when I compile the first one, it failed:

error[E0382]: borrow of moved value: `arr`
   --> src/main.rs:12:29
    |
2   | fn ppv(arr: &mut Vec<i32>) {
    |        --- move occurs because `arr` has type `&mut Vec<i32>`, which does not implement the `Copy` trait
...
7   |     for &mut val in arr {
    |                     --- `arr` moved due to this implicit call to `.into_iter()`
...
12  |     println!("arr is {:?}", &arr);
    |                             ^^^^ value borrowed here after move
    |

Why is that? Is it interpreting it differently? First case will implicit call into_iter(), it failed, when I call into_iter(), it passed. What happened?

Upvotes: 12

Views: 3025

Answers (1)

user4815162342
user4815162342

Reputation: 155406

I believe the difference is in reborrow, performed in the latter case, but not the former.

Mutable references are normally not Copy. This is by design, as copying a mutable reference would allow mutable aliasing. But then, the question is how does this work:

fn foo(v: &mut Vec<i32>) {
    v.push(1);  // equivalent to Vec::push(v, 1)
    v.push(2);  // equivalent to Vec::push(v, 2)
}

If the first call to push() receives v, which is not Copy, then the second call to push() should fail to compile with "use of moved value". And yet it compiles, as does the desugared version that invokes Vec::push() explicitly.

The reason it compiles is that the compiler automatically performs a trick referred to as "reborrowing", where it replaces the reference v with the equivalent reference &mut *v. This transformation is done both on the receiver (self) and the function arguments:

// v is &mut Vec<i32>
v.push(1);        // treated as (&mut *v).push(1)
Vec::push(v, 2);  // treated as Vec::push(&mut *v, 2)

The reborrowed form compiles because it creates a fresh temporary mut reference projected from an existing mut reference. This is the same mechanism that allows you to create &mut r.some_field given an r: &mut SomeStruct. That doesn't violate mutable aliasing prohibition as long as the temporary reference lives shorter than the original, and you don't attempt to use the original reference while the temporary one is live.

One typically becomes aware of reborrowing in the exceptional cases when it fails. This answer describes such case with serde, where reborrowing failed due to use of generics.

To get back to the original example, your for loop is another example of reborrow failing to happen. Given a mutable reference arr, the difference between for &mut val in arr and for &mut val in arr.into_iter() is that the explicit call to into_iter() is treated as (&mut *arr).into_iter() and thereby allows continued use of arr after the loop. The naked for doesn't do so, and the arr object is lost.

Upvotes: 7

Related Questions