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
借用的!这是不公平的!
这是否应该被视为 rust-lang/rust#6393 的众多骗局之一(现在由 rust-lang/rfcs#811 跟踪?)但那里的讨论似乎集中在使&mut self
和&self
在一个块中共存。
我不能给你一个明确的答案,但我会尝试在这里解释一些事情。让我们从澄清一些事情开始:
编译器知道
drop()
会掉落x
这不是真的。虽然编译器知道标准库中的一些"神奇"东西,但drop()
不是这样的lang 项。事实上,你可以自己实现drop()
,这实际上是最简单的事情:
fn drop<T>(_: T) {}
该函数只是按值获取某些内容(因此,它被移动到drop()
中),并且由于drop()
内部没有任何反应,因此该值在作用域的末尾被删除,就像在任何其他函数中一样。所以:编译器不知道x
被丢弃,它只知道x
被移动。
您可能已经注意到,无论我们是否添加drop()
调用,编译器错误都保持不变。现在,编译器只会在涉及引用时查看变量的范围。来自Niko Matsakis对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 路线图的一部分来解决的。阅读更新的好地方是这里(其中还包含指向Niko的五篇相关博客文章的链接)。
知道
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
"不是魔法"的意思。