为什么`Drop`特性只在作用域结束时执行,而不是在最后一次使用后执行?

huangapple go评论62阅读模式
英文:

Why Drop trait is only executed at the end of the scope, instead of after the last use?

问题

这是来自rust onomicon # lifetime的问题。

第一个例子可以编译,因为x是一个引用,编译器可以推断其生命周期最小到最后的使用位置:println!(),因此x在此行之后被丢弃。

但是当x是实现了Drop特性的结构体时情况就不同了。

在这种情况下,drop()仅在作用域的最末尾执行,因此x保持有效直到最后一行。

但是为什么编译器不能将x的生命周期最小化到最后的使用?当x实现了Drop特性时,是否在最后的使用后立即应用drop()会产生一些非平凡的副作用?

英文:

This is a question from rust onomicon # lifetime

The first example can compile, as x is a reference and the compiler can infer its lifetime as minimal as the last use here :println!(), so x is dropped after this line.

let mut data = vec![1, 2, 3];
let x = &data[0];
println!("{}", x);
// This is OK, x is no longer needed
data.push(4);

But the case is different when x is a struct implemented Drop trait.

#[derive(Debug)]
struct X<'a>(&'a i32);

impl Drop for X<'_> {
    fn drop(&mut self) {}
}

let mut data = vec![1, 2, 3];
let x = X(&data[0]);
println!("{:?}", x);
data.push(4);
// Here, the destructor is run and therefore this'll fail to compile.

The onomicon says in this case, drop() is only executed at the very end of a scope, so x keeps valid until the last line.

But why the compiler cannot minimize the lifetime of x to the last use? And is applying drop() just after the last use has some nontrivial side effects when x is implemented Drop trait?

答案1

得分: 3

主要原因是它曾经被定义为这样,现在不能再改变了,因为这样做不再可能是向后兼容的,可能会破坏一些东西。

您的代码可以通过引入嵌套作用域来轻松修复,这在我看来是这些情况下的最佳实践:

#[derive(Debug)]
struct X<'a>(&'a i32);

impl Drop for X<'_> {
    fn drop(&mut self) {}
}

fn main() {
    let mut data = vec![1, 2, 3];
    {
        let x = X(&data[0]);
        println!("{:?}", x);
    }
    data.push(4);
}
X(1)

或者,您可以手动drop它:

#[derive(Debug)]
struct X<'a>(&'a i32);

impl Drop for X<'_> {
    fn drop(&mut self) {}
}

fn main() {
    let mut data = vec![1, 2, 3];

    let x = X(&data[0]);
    println!("{:?}", x);
    drop(x);

    data.push(4);
}
X(1)
英文:

The primary reason is that it was once defined to be like that, and now changing it isn't possible any more because it wouldn't be backwards-compatible and might break stuff.

Your code is easily fixable by introducing a nested scope, though, which is (to my understanding) best practice in those situations:

#[derive(Debug)]
struct X&lt;&#39;a&gt;(&amp;&#39;a i32);

impl Drop for X&lt;&#39;_&gt; {
    fn drop(&amp;mut self) {}
}

fn main() {
    let mut data = vec![1, 2, 3];
    {
        let x = X(&amp;data[0]);
        println!(&quot;{:?}&quot;, x);
    }
    data.push(4);
}
X(1)

Alternatively, you could drop it manually:

#[derive(Debug)]
struct X&lt;&#39;a&gt;(&amp;&#39;a i32);

impl Drop for X&lt;&#39;_&gt; {
    fn drop(&amp;mut self) {}
}

fn main() {
    let mut data = vec![1, 2, 3];

    let x = X(&amp;data[0]);
    println!(&quot;{:?}&quot;, x);
    drop(x);

    data.push(4);
}
X(1)

huangapple
  • 本文由 发表于 2023年6月1日 18:12:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76380847.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定