Reputation: 155
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
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