移动变量在调用"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借用的!这是不公平的!

这是否应该被视为 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"不是魔法"的意思。

最新更新