То, что вы делаете, это доступ к полю по указателю.
Проверка Выражение доступа к полю :
, если тип выражения слева отточка является указателем, она автоматически разыменовывается столько раз, сколько необходимо для обеспечения доступа к полю
Пример того, как 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
.В этой ситуации:
- Rust попытается скопировать значение
d.id
.Так как d.id
равен i32
и реализует черту Copy
, он скопирует значение в id
.
Рассмотрите этот код:
fn p(mut d: &Data) {
let id = d.id; // works
let body = d.body; // fails
}
Этот код не будет работать, потому что:
- Rust попытается скопировать
d.body
, но Vec<i32>
не имеет реализации черты Copy
. - 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
.
Эта реализация возвращает итератор, который является итератором-потребителем.Это означает, что ваш цикл будет владеть собственными элементами , а не их ссылками.Для потребляющей части этой реализации нужен фактический вектор, а не ссылка, поэтому происходит перемещение.