我以为我有了移动语义的想法,直到这段代码 .
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
我通过了一个引用,我通过auto-deref功能访问了一个字段,为什么它是一个移动?
3 回答
你正在做的是在指针上进行字段访问 .
检查Field Access Expression:
Rust如何评估借用内容上的字段访问表达式的示例:
注意:d仍然是借来的内容,只需要auto deref来访问这些字段 .
为什么要搬家?
考虑以下代码:
此代码将按预期工作,此处不会有任何移动,因此您可以重用
d.id
. 在这种情况下:d.id
的值 . 由于d.id
是i32
并实现Copy
特征,因此它会将值复制到id
.考虑以下代码:
此代码无效,因为:
Rust将尝试复制
d.body
,但Vec<i32>
没有Copy
特征的实现 .Rust将尝试从
d
移动body
,您将收到"cannot move out of borrowed content"错误 .这对循环有何影响?
来自the reference
这意味着您的向量必须具有
IntoIterator
的实现,因为IntoIterator::into_iter(self)
期望self
作为参数 . 幸运的是,两个impl IntoIterator for Vec<T>,另一个是impl<'a, T> IntoIterator for &'a Vec<T>存在 .为什么会这样?
只是:
&d.body
时,您的循环使用IntoIterator
的&Vec
实现 .此实现返回一个指向矢量切片的迭代器 . 这意味着你将从你的向量中获得 reference of elements .
d.body
时,您的循环使用Vec
的Vec
实现 .此实现返回一个迭代器,它是一个消耗迭代器 . 这意味着你的循环将具有 ownership of actual elements ,而不是它们的引用 . 对于消费部分,此实现需要实际向量而不是引用,因此移动发生 .
您正在访问
d
中的body
字段 .body
本身是Vec<i32>
,不是参考 . 如果直接使用d
,则不需要&
,但由于您正在访问d
中的字段,因此必须指定要对该字段进行引用 .基本上
d
拥有body
. 如果你借d
你不能偷body
,它属于d
但你可以借用它 .这个循环将是desugared into something similar to the following:
如您所见,它正在使用
into_iter
,它会移动向量d.body
.