Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

rust

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?

like image 693
ban Avatar asked Aug 26 '21 09:08

ban


1 Answers

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 do the desugared versions.

The reason it compiles is that the compiler automatically performs reborrowing, replacing v with &mut *v. In other words. 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 is allowed to create a temporary mutable reference based on an existing mutable reference (e.g. through &mut r.some_field where r is &mut SomeStruct), as long as the temporary reference lives shorter than the original, and you don't use the original reference while the temporary one is live.

You typically notice reborrowing only in the rare 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.

like image 141
user4815162342 Avatar answered Oct 29 '22 00:10

user4815162342