我以为我得到了移动语义的想法,直到这段代码。
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>,
}
Run Code Online (Sandbox Code Playgroud)
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>,
}
Run Code Online (Sandbox Code Playgroud)
我传递了一个引用,并通过自动取消引用功能访问了一个字段,那么为什么要这样做呢?
您正在做的是对指针进行字段访问。
检查字段访问表达式:
如果点左边的表达式类型是指针,它会根据需要自动取消引用多次,以使字段访问成为可能
Rust 如何评估借用内容的字段访问表达式的示例:
let d = Data { /*input*/}
let body = (&d).body // -> (*&d).body -> d.body
let ref_body = &(&d).body // -> &(*&).body -> &d.body -> &(d.body)
Run Code Online (Sandbox Code Playgroud)
注意 : d 仍然是借用的内容,只需要 auto deref 来访问字段。
考虑这个代码:
struct Data {
body: Vec<i32>,
id: i32,
}
fn p(mut d: &Data) {
let id = d.id;
}
Run Code Online (Sandbox Code Playgroud)
此代码将按预期工作,此处不会有任何移动,因此您将能够重用d.id. 在这种情况下:
d.id. 由于d.idisi32并实现了Copytrait,它会将值复制到id.考虑这个代码:
fn p(mut d: &Data) {
let id = d.id; // works
let body = d.body; // fails
}
Run Code Online (Sandbox Code Playgroud)
此代码将不起作用,因为:
d.body但Vec<i32>没有实现Copytrait。body从 移出d,您将收到“无法移出借用的内容”错误。从参考
甲
for表达是遍历元件语法构造通过的实施方案提供std::iter::IntoIteratorfor 循环等效于以下块表达式。
Run Code Online (Sandbox Code Playgroud)'label: for PATTERN in iter_expr { /* loop body */ }相当于
Run Code Online (Sandbox Code Playgroud){ 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实现IntoIterator。此实现返回一个指向向量切片的迭代器。这意味着您将从您的向量中获得元素的引用。
d.body,您的循环使用 的Vec实现IntoIterator。此实现返回一个迭代器,它是一个消耗迭代器。这意味着您的循环将拥有实际元素的所有权,而不是它们的引用。对于消费部分,此实现需要实际向量而不是引用,因此会发生移动。