调用`drop`后移动的变量仍在借用?

nod*_*kai 5 lifetime rust

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
Run Code Online (Sandbox Code Playgroud)

编译器知道drop()丢弃x(从注释掉的代码中的错误可以看出),但仍然认为变量是借用的a!这不公平!

这应该被认为是众多生锈/锈病#6393之一(现在由rust-lang/rfcs#811跟踪?)但是那里的讨论似乎集中在一个块中制作&mut self&self共存.

Luk*_*odt 8

我不能给你一个明确的答案,但我会试着在这里解释一些事情.让我们从澄清一些事情开始:

编译器知道drop()丢弃x

这不是真的.虽然编译器知道标准库中有一些"神奇"的东西,但这drop()不是一个lang项目.事实上,你可以实现drop()自己,这实际上是最简单的事情:

fn drop<T>(_: T) {}
Run Code Online (Sandbox Code Playgroud)

该函数只是按值取值(因此,它被移入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
Run Code Online (Sandbox Code Playgroud)

我猜你已经知道了这一切,但是你可以通过交换线路解决您的例子let mut x ...;let a ...;.


我不确定这个确切的问题是否可以通过任何目前提出的解决方案来解决.但我希望我们能尽快看到,因为所有这些都是Rust 2017路线图的一部分.对更新读了一个不好的地方就是这里(其中还包含指向尼科的五个相关博客文章).

  • @nodakai我不太确定你在这次讨论中想要实现的目标:/据我所见,你的问题得到了回答.您的评论看起来像是想要反对我,而不是试图理解.我的说法'drop()`并不特别,这当然是正确的,因为即使是文档也清楚地说明了这一点.在不使用不安全的代码时,函数是否总是必须丢弃或返回拥有的值,我认为这是一个有趣的问题!但是我们不应该在评论中对它进行探讨,而是在这里提出一个新问题.顺便说一句:`Vec <&i32>`实现`发送'. (4认同)

Pet*_*all 5

编译器知道drop()丢弃x(从注释掉的代码中的错误可以看出)

Rust编译器不知道任何关于drop它的内容和它的作用.它只是一个库函数,它可以用它所做的任何东西,因为它现在拥有它.

drop正如文档所指出的那样,定义只是:

fn drop<T>(_x: T) { }
Run Code Online (Sandbox Code Playgroud)

它起作用,因为它将参数移入函数中,因此在函数完成时由编译器自动删除.

如果您创建自己的函数,您将得到完全相同的错误消息:

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();
}
Run Code Online (Sandbox Code Playgroud)

这正是文档中所说的drop"不是魔术"的含义.