Привязка переменных: перемещение &mu или заимствование референта?
Этот код не работает, как и ожидалось в let c = a;
с ошибкой компиляции "использование перемещенного значения: a
":
fn main() {
let a: &mut i32 = &mut 0;
let b = a;
let c = a;
}
a перемещен в b и больше не доступен для назначения c. Все идет нормально.
Однако, если я просто аннотирую b
набери и оставь все остальное в покое
fn main() {
let a: &mut i32 = &mut 0;
let b: &mut i32 = a;
let c = a;
}
код снова не работает в let c = a;
Но на этот раз с совершенно другим сообщением об ошибке: "не может выйти из a
потому что это заимствовано... заимствовать *a
происходит здесь: let b: &mut i32 = a;
"
Итак, если я просто аннотировать b
тип: без движения a
в b
, но вместо этого "повторно"-заем *a
?
Что мне не хватает?
Приветствия.
2 ответа
Итак, если я просто аннотировать
b
тип: без движенияa
вb
, но вместо этого "повторно"-заем*a
?Что мне не хватает?
Абсолютно ничего, так как в этом случае эти две операции семантически очень похожи (и эквивалентны, если a
а также b
принадлежат к той же области).
- Либо вы перемещаете ссылку
a
вb
, делаяa
перемещенное значение, и больше не доступно. - Либо вы перезагружаете
*a
вb
, делаяa
непригодный до тех пор, покаb
находится в сфере.
Второй случай менее определен, чем первый, вы можете показать это, поместив строку, определяющую b
в подпространство.
Этот пример не скомпилируется, потому что a
перемещен:
fn main() {
let a: &mut i32 = &mut 0;
{ let b = a; }
let c = a;
}
Но этот будет, потому что однажды b
выходит за рамки a
разблокирован:
fn main() {
let a: &mut i32 = &mut 0;
{ let b = &mut *a; }
let c = a;
}
Теперь к вопросу "Почему происходит аннотирование типа b
изменить поведение?", я думаю, будет:
- Когда нет аннотации типа, операция представляет собой простой и простой ход. Ничего не нужно проверять.
- При наличии аннотации типа может потребоваться преобразование (приведение
&mut _
в&_
или преобразование простой ссылки в ссылку на объект признака). Таким образом, компилятор выбирает повторное заимствование значения, а не ход.
Например, этот код корректно действителен:
fn main() {
let a: &mut i32 = &mut 0;
let b: &i32 = a;
}
и здесь движется a
в b
не имеет никакого смысла, так как они разного типа. Еще этот код компилируется: b
просто повторно заимствует *a
и значение не будет изменчиво доступно через a
пока b
находится в сфере.
Чтобы дополнить ответ @Levans на конкретный вопрос «Почему аннотирование типа меняет поведение?»:
Когда вы не пишете тип, компилятор выполняет простое перемещение. Когда вы указываете тип,let
заявление становится сайтом принуждения, как описано в разделе «Сайты принуждения»:
Возможные сайты принуждения:
- операторы let, в которых задан явный тип.
В данном случае компилятор выполняет повторное заимствование, которое является частным случаем принуждения, идущего от к&mut
, как поясняется в комментарии к этому выпуску на GitHub.
Обратите внимание, что повторное заимствование в целом и принуждение к повторному заимствованию в частности в настоящее время плохо документированы. В справочнике Rust есть открытая проблема , чтобы улучшить этот момент.