首页 文章

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

提问于
浏览
5
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() drop x (从注释掉的代码中的错误可以看出),但仍然认为变量是从 a 借来的!这不公平!

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

2 回答

  • 8

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

    编译器知道drop()drop x

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

    fn drop<T>(_: T) {}
    

    该函数只是按值取值(因此,它被移入 drop() )并且由于 drop() 内部没有任何反应,因此该值在范围的末尾被删除,就像在任何其他函数中一样 . 所以:编译器不知道 x 被删除了,它只知道 x 被移动了 .


    您可能已经注意到,无论我们是否添加 drop() 调用,编译器错误都保持不变 . 现在,编译器只会在引用时查看变量的范围 . 从Niko Matsakis' intro to NLL

    编译器当前的工作方式,将引用分配给变量意味着它的生命周期必须与该变量的整个范围一样大 .

    并在a later blog post of his

    特别是,今天,一旦一生必须超出单一陈述的范围[...],它必须一直延伸到封闭区块的末尾 .

    这正是这里发生的事情,所以是的,你的问题与所有这些"lexical borrowing"的东西有关 . 从当前编译器的角度来看,表达式 &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路线图的一部分得到解决 . 阅读更新的好地方是here(其中还包含Niko的五篇相关博客文章的链接) .

  • 5

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

    Rust编译器对 drop 以及它的作用一无所知 . 它只是一个库函数,它可以用它的 Value 做任何事情,因为它现在拥有它 .

    正如文档所指出的那样, 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();
    }
    

    这正是it says drop "isn't magic"文档中的含义 .

相关问题