gpt4 book ai didi

rust - 为什么通过 auto-deref 访问的引用变量会被移动?

转载 作者:行者123 更新时间:2023-11-29 08:13:45 25 4
gpt4 key购买 nike

在这段代码之前,我以为我了解了移动语义。

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

我传递了一个引用,并且我通过自动取消引用功能访问了一个字段,那么为什么它是一个移动?

最佳答案

您正在做的是对指针进行字段访问。

检查 Field Access Expression :

if the type of the expression to the left of the dot is a pointer, it is automatically dereferenced as many times as necessary to make the field access possible

Rust 如何评估借用内容的字段访问表达式的示例:

let d = Data { /*input*/}
let body = (&d).body // -> (*&d).body -> d.body
let ref_body = &(&d).body // -> &(*&).body -> &d.body -> &(d.body)

注意:d 仍然是借用的内容,只需要 auto deref 来访问字段。


为什么要搬家?

考虑这段代码:

struct Data {
body: Vec<i32>,
id: i32,
}

fn p(mut d: &Data) {
let id = d.id;
}

此代码将按预期工作,此处不会有任何移动,因此您将能够重用 d.id .在这种情况下:

  1. Rust 会尝试复制 d.id 的值.自 d.idi32并实现 Copy trait,它会将值复制到 id .

考虑这段代码:

fn p(mut d: &Data) {
let id = d.id; // works
let body = d.body; // fails
}

此代码将不起作用,因为:

  1. Rust 会尝试复制 d.body但是Vec<i32>没有执行 Copy特征。
  2. Rust 将尝试移动 body来自 d ,您将收到“无法移出借用的内容”错误。

这对循环有何影响?

来自 the reference

A for expression is a syntactic construct for looping over elements provided by an implementation of std::iter::IntoIterator

A for loop is equivalent to the following block expression.

'label: for PATTERN in iter_expr {
/* loop body */
}

is equivalent to

{
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 .

此实现返回一个迭代器,它是一个消耗迭代器。这意味着您的循环将拥有实际元素的所有权,而不是它们的引用。对于消费部分,此实现需要实际向量而不是引用,因此会发生移动。

关于rust - 为什么通过 auto-deref 访问的引用变量会被移动?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53692702/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com