我以为直到此代码之前我都想到了移动语义。
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
我通过了引用,并且通过自动取消引用功能访问了一个字段,所以为什么要这样做?
答案 0 :(得分:4)
您正在做的是在指针上进行字段访问。
如果点左侧表达式的类型是指针,则它 被自动取消引用的次数,以使 可以现场访问
Rust如何评估借阅内容的字段访问表达式的示例:
let d = Data { /*input*/}
let body = (&d).body // -> (*&d).body -> d.body
let ref_body = &(&d).body // -> &(*&).body -> &d.body -> &(d.body)
注意:d仍是借来的内容,仅需自动deref即可访问字段。
考虑以下代码:
struct Data {
body: Vec<i32>,
id: i32,
}
fn p(mut d: &Data) {
let id = d.id;
}
此代码将按预期工作,并且此处没有任何移动,因此您可以重用d.id
。在这种情况下:
d.id
的值。由于d.id
是i32
并实现了Copy
特征,它将把值复制到id
。 考虑以下代码:
fn p(mut d: &Data) {
let id = d.id; // works
let body = d.body; // fails
}
此代码将不起作用,因为:
d.body
,但是Vec<i32>
没有实现Copy
特性。 body
从d
中移出,您将收到“无法移出借用的内容”错误。
for
表达式是一种语法构造,用于循环遍历std::iter::IntoIterator
的实现所提供的元素for循环等效于以下块表达式。
'label: for PATTERN in iter_expr { /* loop body */ }
等同于
{ let result = match IntoIterator::into_iter(iter_expr) { mut iter => 'label: loop { let mut next; match Iterator::next(&mut iter) { Option::Some(val) => next = val, Option::None => break, }; let PAT = next; let () = { /* loop body */ }; }, }; result }
这意味着您的向量必须具有IntoIterator
的实现,因为IntoIterator::into_iter(self)
期望self
作为参数。幸运的是,两个impl IntoIterator for Vec<T>
和另一个impl<'a, T> IntoIterator for &'a Vec<T>
都存在。
简单地:
&d.body
时,循环使用&Vec
的{{1}}实现。此实现返回一个迭代器,该迭代器指向向量的切片。这意味着您将从向量中获得元素的引用。
IntoIterator
时,循环使用d.body
的{{1}}实现。此实现返回一个迭代器,它是一个消耗迭代器。这意味着您的循环将具有实际元素的所有权,而不是其引用。对于消费部分,此实现需要实际矢量而不是参考,因此会发生移动。
答案 1 :(得分:0)
您正在访问body
中的字段d
。 body
本身是Vec<i32>
,不是参考。如果直接使用d
,则不需要&
,但是由于要访问d
中的字段,因此必须指定要引用该字段的内容。 / p>
基本上d
拥有body
。如果您借用d
,则无法盗用body
,它属于d
,但您可以借用。
答案 2 :(得分:-1)
此循环为desugared into something similar to the following:
let mut iter = IntoIterator::into_iter(v);
loop {
match iter.next() {
Some(x) => {
// loop body
},
None => break,
}
}
如您所见,它使用into_iter
来移动向量d.body
。