在实现Drop trait后的Rust Drop调用顺序

3

我是 Rust 的新手。我的简单代码:

struct Foo<'a>{
    data : & 'a String,
}
fn test_foo(){
   let s1:String = String::from("A"); 
   let mut foo:Foo = Foo { data: & s1 };
   let s2:String = String::from("B");
   foo.data = & s2;  
}

我的问题:如果我为Foo结构体实现Drop特质

foo.data = & s2; 

开始出现编译错误:s2 不够长久。这个错误对我来说是有意义的。但是,如果我没有实现 FooDrop 特质,为什么我就没有得到同样的错误呢?


这个回答解决了你的问题吗?什么是非词法生命周期? - Jmb
同样相关:https://dev59.com/ksXsa4cB1Zd3GeqPbFm_ - Jmb
1个回答

1
在Rust中,同一作用域中的本地变量按照定义顺序的相反顺序进行丢弃。
fn test_foo(){
   let s1:String = String::from("A"); 
   let mut foo:Foo = Foo { data: & s1 };
   let s2:String = String::from("B");
   foo.data = & s2;  
}

我们在此声明三个本地变量:依次为s1foos2。Rust希望以相反的顺序将它们删除:s2先,然后是foo,最后是s1。但是,生命周期存在问题。一旦我们删除了s2,那么foo.data就是未初始化的,即它指向垃圾内存。

现在,没有Drop为什么可以工作呢?Rust有一个叫做部分移动(partial moves)的概念。如果你有一个具有多个字段的结构体,在不使整个结构体无效的情况下,Rust允许你移出某些字段。原则上,如果我有一个

struct Person {
  name: String,
  age: i32,
  occupation: String,
}

如果我执行了 let name = my_person.name; (其中 my_person: Person),那么我已经将值移出了一个 Person。因此,my_person.name 是无效的,并且在部分移动不存在的情况下,Rust 应该认为 my_person 是完全无效的。但是,我们知道 my_person.agemy_person.occupation 仍然有效,因此 Rust 将允许 ageoccupation 保持原样。它记得 name 被移动了(因此是垃圾,不应该被丢弃),而 ageoccupation 仍然有效。
在你的示例中,同样的事情正在发生。Rust 想要丢弃 s2,但 Foo 仍然持有对它的引用。Rust 认为这没问题:我们将丢弃 s2 并简单地说 Foo 已经被部分移动:它的 data 字段不再有效。然后当我们下一步去丢弃 foo 时,我们不需要丢弃引用,只需要丢弃最外层的 Foo 层本身。
如果没有 Drop 实例,这是可以的,Rust 将允许它。但是,如果 impl<'a> Drop for Foo<'a> 在作用域内,则对于 Foo,部分移动将被完全禁用。Rust 看到你正在实现一些自定义的 Drop 行为,现在它不会允许部分初始化的对象存在,因为我们必须丢弃部分初始化的对象,而 Rust 无法预测你的自定义 Drop 代码将要做什么或者它将要做出什么样的假设。
因此,使用 Drop 实现时,Rust 仍然想先丢弃 s2,但它不能部分地移动引用出 foo,因为那会使 foo 处于部分初始化状态,这是不允许的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接