Перемещенная переменная все еще заимствует после вызова `drop`?
fn main() {
let mut x: Vec<&i32> = vec![];
let a = 1;
x.push(&a);
drop(x);
// x.len(); // error[E0382]: use of moved value: `x`
} // `a` dropped here while still borrowed
Компилятор знает drop()
капли x
(как видно из ошибки в закомментированном коде), но все еще думает, что переменная заимствует из a
! Это нечестно!
Стоит ли рассматривать это как один из многочисленных логов ржавчины-ланга / ржавчины №6393 (который сейчас отслеживается ржавчиной-лангом / rfcs # 811?) Но, похоже, обсуждение здесь сосредоточено на создании &mut self
а также &self
сосуществовать в одном блоке.
2 ответа
Я не могу дать вам однозначный ответ, но постараюсь объяснить несколько вещей здесь. Давайте начнем с разъяснения чего-то:
Компилятор знает
drop()
каплиx
Это неправда. Хотя в стандартной библиотеке есть несколько "волшебных" вещей, о которых знает компилятор, drop()
не такой ланг предмет. На самом деле, вы могли бы реализовать drop()
себя, и это на самом деле проще всего сделать:
fn drop<T>(_: T) {}
Функция просто берет что-то по значению (таким образом, она перемещается в drop()
) и так как внутри ничего не происходит drop()
это значение удаляется в конце области, как и в любой другой функции. Итак: компилятор не знает x
упал, он просто знает x
перемещен
Как вы могли заметить, ошибка компилятора остается неизменной независимо от того, добавляем ли мы drop()
вызов. Прямо сейчас, компилятор будет смотреть на область действия переменной, только когда дело касается ссылок. Из вступления Нико Мацакиса в NLL:
То, как в настоящее время работает компилятор, присваивая ссылку на переменную, означает, что его время жизни должно быть таким же большим, как и вся область действия этой переменной.
И в более поздней записи его блога:
В частности, сегодня, когда время жизни должно выходить за пределы одного оператора [...], оно должно доходить до конца окружающего блока.
Это именно то, что здесь происходит, так что да, ваша проблема связана со всем этим "лексическим заимствованием". С точки зрения текущей компиляции, время жизни выражения &a
должен быть по крайней мере такой же большой, как объем x
, Но это не работает, так как ссылка будет жить a
, поскольку сфера x
больше, чем объем a
как указано компилятором:
= note: values in a scope are dropped in the opposite order they are created
И я думаю, вы уже все это знаете, но вы можете исправить свой пример, поменяв строки let mut x ...;
а также let a ...;
,
Я не уверен, будет ли эта точная проблема решена любым из предложенных в настоящее время решений. Но я надеюсь, что мы увидим это достаточно скоро, поскольку все это рассматривается в рамках дорожной карты Rust 2017. Хорошее место, чтобы прочитать об обновлениях здесь (которое также содержит ссылки на пять соответствующих сообщений в блоге Нико).
Компилятор знает
drop()
каплиx
(как видно из ошибки в закомментированном коде)
Компилятор Rust ничего не знает о drop
и что это делает. Это просто библиотечная функция, которая может делать все, что угодно, со значением, поскольку теперь оно принадлежит ей.
Определение drop
Как указывает документация, буквально просто:
fn drop<T>(_x: T) { }
Это работает, потому что аргумент перемещается в функцию, и поэтому автоматически отбрасывается компилятором, когда функция завершает работу.
Если вы создадите свою собственную функцию, вы получите точно такое же сообщение об ошибке:
fn my_drop<T>(_x: T) { }
fn main() {
let mut x: Vec<&i32> = vec![];
let a = 1;
x.push(&a);
my_drop(x);
x.len();
}
Это именно то, что подразумевается в документации, когда говорится drop
"не волшебство".