为什么移动了通过自动取消引用访问的引用变量? [英] Why is a reference variable accessed via auto-deref moved?

查看:44
本文介绍了为什么移动了通过自动取消引用访问的引用变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我以为我得到了移动语义的想法,直到这段代码.

fn main() {让 v = 数据 {正文: vec![10, 40, 30],};p(&v);}fn p(d: &Data) {因为我在 d.body {//&d.body, 为什么 d.body 移动?println!("{}", i);}}结构数据{主体:Vec i32 ,}

error[E0507]: 不能移出借来的内容-->src/main.rs:9:14|9 |因为我在 d.body {|^^^^^^不能移出借来的内容错误[E0507]:无法移出`&`引用后面的`d.body`-->src/main.rs:9:14|8 |fn p(d: &Data) {|----- 帮助:考虑将其更改为可变引用:`&mut Data`9 |因为我在 d.body {|^^^^^^|||无法移出位于 `&` 引用后面的 `d.body`|`d` 是一个 `&` 引用,所以它引用的数据不能被移动

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

解决方案

你正在做的是指针上的字段访问.

检查字段访问表达式:

<块引用>

如果点左边的表达式的类型是指针,它根据需要自动取消引用多次,以使可以现场访问

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

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

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

<小时>

为什么要搬家?

考虑这个代码:

struct 数据 {主体:Vec i32 ,编号:i32,}fn p(mut d: &Data) {让 id = d.id;}

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

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

考虑这个代码:

fn p(mut d: &Data) {让 id = d.id;//有效让身体 = d.body;//失败}

此代码不起作用,因为:

  1. Rust 会尝试复制 d.bodyVec 没有实现 Copy 特性.
  2. Rust 会尝试从 d 移动 body,你会得到无法移出借来的内容"错误.

这对循环有何影响?

来自参考 <块引用>

for 表达式是一种语法构造,用于循环由 std::iter::IntoIterator

的实现提供的元素

for 循环相当于下面的块表达式.

'label: for PATTERN in iter_expr {/* 循环体 */}

相当于

<代码>{让结果 = 匹配 IntoIterator::into_iter(iter_expr) {mut iter =>'标签:循环{让 mut 接下来;匹配迭代器::下一个(&mut iter) {选项::一些(val) =>下一个 = val,选项::无 =>休息,};让 PAT = 下一个;let () = {/* 循环体 */};},};结果}

这意味着您的向量必须具有 IntoIterator 的实现,因为 IntoIterator::into_iter(self) 需要 self 作为参数.幸运的是,Itcode>;T>,另一个是 impl<'a, T>IntoIterator for &'a Vec 存在.

为什么会这样?

简单地说:

  • 当您使用 &d.body 时,您的循环将使用 IntoIterator&Vec 实现.

此实现返回一个迭代器,它指向您的向量切片.这意味着您将从您的向量中获得元素的引用.

  • 当您使用 d.body 时,您的循环将使用 IntoIteratorVec 实现.

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

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?

解决方案

What you are doing is field accessing on pointer.

Check 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

Sample for how Rust evaluates Field Access Expression on Borrowed Content :

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

Note : d is still borrowed content, auto deref is just needed to access the fields.


Why move ?

Consider this code:

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

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

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:

  1. Rust will try to copy the value of d.id. Since d.id is i32 and implements the Copy trait, it will copy the value to id.

Consider this code:

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

This code will not work because:

  1. Rust will try to copy d.body but Vec<i32> has no implementation of the Copy trait.
  2. Rust will try to move body from d, and you will get the "cannot move out of borrowed content" error.

How does this effect the loop?

From 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
}

This means your vector must have an implementation of IntoIterator because IntoIterator::into_iter(self) expects self as an argument. Luckily, both impl IntoIterator for Vec<T>, another is impl<'a, T> IntoIterator for &'a Vec<T> exist.

Why does this happen?

Simply:

  • When you use &d.body, your loop uses the &Vec implementation of IntoIterator.

This implementation returns an iterator which points at your vector's slice. This means you will get the reference of elements from your vector.

  • When you use d.body, your loop uses the Vec implementation of IntoIterator.

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.

这篇关于为什么移动了通过自动取消引用访问的引用变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆