Почему ссылочная переменная доступна через auto-deref? - PullRequest
0 голосов
/ 09 декабря 2018

Я думал, что получил идею семантики перемещения до этого кода.

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

Я передал ссылку, и я получил доступ к полю через функцию автоматической разыменования, так почему это движение

Ответы [ 3 ]

0 голосов
/ 09 декабря 2018

Вы получаете доступ к полю body в d.body сам по себе является Vec<i32>, который не является ссылкой.Если вы будете использовать d напрямую, нет необходимости в &, но, поскольку вы получаете доступ к полю в d, вы должны указать, что хотите иметь ссылку на поле.

В основномd владеет body.Если вы одолжите d, вы не сможете украсть body, он принадлежит d, но вы можете одолжить его.

0 голосов
/ 09 декабря 2018

То, что вы делаете, это доступ к полю по указателю.

Проверка Выражение доступа к полю :

, если тип выражения слева отточка является указателем, она автоматически разыменовывается столько раз, сколько необходимо для обеспечения доступа к полю

Пример того, как Rust оценивает выражение доступа к полю для заимствованного содержимого:

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

Примечание: d по-прежнему является заимствованным содержимым, для доступа к полям просто требуется автоматическое разыменование.


Зачем двигаться?

Рассмотрите этот код:

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

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

Thisкод будет работать как положено, и здесь не будет никаких шагов, поэтому вы сможете повторно использовать d.id.В этой ситуации:

  1. Rust попытается скопировать значение d.id.Так как d.id равен i32 и реализует черту Copy, он скопирует значение в 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, и вы получите сообщение об ошибке «невозможно удалить из заимствованного содержимого».

Как это влияет на цикл?

С ссылка

A 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 реализацию IntoIterator.

Эта реализация возвращает итератор, который указывает на срез вашего вектора.Это означает, что вы получите ссылку на элементы из вашего вектора.

  • Когда вы используете d.body, ваш цикл использует реализацию Vec IntoIterator.

Эта реализация возвращает итератор, который является итератором-потребителем.Это означает, что ваш цикл будет владеть собственными элементами , а не их ссылками.Для потребляющей части этой реализации нужен фактический вектор, а не ссылка, поэтому происходит перемещение.

0 голосов
/ 09 декабря 2018

Этот цикл будет разбит на что-то похожее на следующее :

let mut iter = IntoIterator::into_iter(v);
loop {
    match iter.next() {
        Some(x) => {
            // loop body
        },
        None => break,
    }
}

Как вы можете видеть, он использует into_iter, который перемещает вектор d.body.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...