Привязка переменных: перемещение &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 есть открытая проблема , чтобы улучшить этот момент.

Другие вопросы по тегам