Что означает ошибка в этом случае:
fn main() {
let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
v[v[1]] = 999;
}
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/main.rs:3:7
|
3 | v[v[1]] = 999;
| --^----
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
| mutable borrow later used here
Я обнаружил , что индексация осуществляется через Index
и IndexMut
черт , и что v[1]
является синтаксический *v.index(1)
. С этими знаниями я попытался запустить следующий код:
use std::ops::{Index, IndexMut};
fn main() {
let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
*v.index_mut(*v.index(1)) = 999;
}
К моему удивлению, это работает без нареканий! Почему первый фрагмент не работает, а второй работает? Как я понимаю документацию, они должны быть эквивалентны, но это, очевидно, не так.
rust
borrow-checker
Лукас Бук
источник
источник
Ответы:
Версия desugared немного отличается от того, что у вас есть. Линия
на самом деле desugars к
Это приводит к тому же сообщению об ошибке, но аннотации дают подсказку о том, что происходит:
Важным отличием вашей исходной версии является порядок оценки. Аргументы вызова функции оцениваются слева направо в указанном порядке до фактического вызова функции. В этом случае это означает, что сначала
&mut v
оценивается, заимствуя заимствованияv
. ДалееIndex::index(&v, 1)
следует оценить, но это невозможно -v
уже заимствовано. Наконец, компилятор показывает, что изменяемая ссылка все еще необходима для вызова функцииindex_mut()
, поэтому изменяемая ссылка все еще жива, когда делается попытка использования общей ссылки.Версия, которая на самом деле компилируется, имеет немного другой порядок оценки.
Во-первых, аргументы функции для вызовов метода оцениваются слева направо, т.е.
*v.index(1)
оцениваются первыми. Это приводит кusize
, что временный общий заемv
может быть освобожден снова. Затем получательindex_mut()
оценивается, тоv
есть заимствован. Это работает нормально, поскольку общий заем уже завершен, и все выражение проходит проверку заимствования.Обратите внимание, что версия, которая компилируется, делает это только после введения «нелексических времен жизни». В более ранних версиях Rust общее заимствование сохранялось до конца выражения и приводило к аналогичной ошибке.
На мой взгляд, самое чистое решение - использовать временную переменную:
источник
*v.index_mut(*v.index_mut(1)) = 999;
сбой с «невозможно одолжить v как изменяемый более одного раза» ~> не должен ли быть компилятор, как если бы он*v.index_mut(*v.index(1)) = 999;
мог выяснить, что внутренний заем больше не нужен?*v.index(1)
является значение, хранящееся в этом индексе, и это значение не требует сохранения заимствованияv
. Результатом*v.index_mut(1)
, с другой стороны, является изменяемое выражение места, которое теоретически может быть назначено, поэтому оно сохраняет заимствование. На первый взгляд, можно поучить средство проверки заимствования, что выражение места в контексте выражения значения можно рассматривать как выражение значения, поэтому возможно, что это скомпилируется в какой-то будущей версии Rust.{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }