I thought I got the idea of move semantics until this code.
fn main() {
let v = Data {
body: vec![10, 40, 30],
};
p(&v);
}
fn p(d: &Data) {
for i in d.body {
// &d.body, Why d.body move?
println!("{}", i);
}
}
struct Data {
body: Vec<i32>,
}
error[E0507]: cannot move out of borrowed content
--> src/main.rs:9:14
|
9 | for i in d.body {
| ^^^^^^ cannot move out of borrowed content
error[E0507]: cannot move out of `d.body` which is behind a `&` reference
--> src/main.rs:9:14
|
8 | fn p(d: &Data) {
| ----- help: consider changing this to be a mutable reference: `&mut Data`
9 | for i in d.body {
| ^^^^^^
| |
| cannot move out of `d.body` which is behind a `&` reference
| `d` is a `&` reference, so the data it refers to cannot be moved
I passed a reference, and I accessed a field via auto-deref feature, so why is it a move?
This loop will be desugared into something similar to the following:
As you can see, it's using
into_iter
, which moves the vectord.body
.What you are doing is field accessing on pointer.
Check Field Access Expression :
Sample for how Rust evaluates Field Access Expression on Borrowed Content :
Note : d is still borrowed content, auto deref is just needed to access the fields.
Why move ?
Consider this code:
This code will work as expected and there will be no moves in here so you will able to reuse
d.id
. In this situation:d.id
. Sinced.id
isi32
and implements theCopy
trait, it will copy the value toid
.Consider this code:
This code will not work because:
d.body
butVec<i32>
has no implementation of theCopy
trait.body
fromd
, and you will get the "cannot move out of borrowed content" error.How does this effect the loop?
From the reference
This means your vector must have an implementation of
IntoIterator
becauseIntoIterator::into_iter(self)
expectsself
as an argument. Luckily, bothimpl IntoIterator for Vec<T>
, another isimpl<'a, T> IntoIterator for &'a Vec<T>
exist.Why does this happen?
Simply:
&d.body
, your loop uses the&Vec
implementation ofIntoIterator
.This implementation returns an iterator which points at your vector's slice. This means you will get the reference of elements from your vector.
d.body
, your loop uses theVec
implementation ofIntoIterator
.This implementation returns an iterator which is a consuming iterator. This means your loop will have the ownership of actual elements, not their references. For the consuming part this implementation needs the actual vector not the reference, so the move occurs.
You are accessing the field
body
ind
.body
itself is aVec<i32>
which is not a reference. If you would used
directly, no&
would be necessary, but since you are accessing a field ind
, you must specify that you want to have the reference to the field.Basically
d
ownsbody
. If you borrowd
you cannot stealbody
, it belongs tod
but you can borrow it.