如何在下降时更改值?

How to change a value while dropping?

我目前正在尝试了解 drop 的工作原理。下面的代码崩溃了,我不明白为什么。根据我的理解,std::ptr::write 的使用应该阻止 运行 的析构函数(编辑:原始值,此处:Rc)(在这种情况下,除了内存泄漏之外应该没有什么不好的事情发生)。但它似乎并没有那样做(playpen,用-O0编译)

use std::rc::Rc;
use std::mem;
use std::ptr;

enum Foo {
    Bar(Rc<usize>),
    Baz
}
use Foo::*;

impl Drop for Foo {
    fn drop(&mut self) {
        match *self {
            Bar(_) => {
                unsafe { ptr::write(self, Foo::Baz) }
                //unsafe { mem::forget(mem::replace(self, Foo::Baz)) }
            }
            Baz => ()
        }
    }
}

fn main() {
    let _ = Foo::Bar(Rc::new(23));
}

给出溢出错误:

thread '<main>' panicked at 'arithmetic operation overflowed', /Users/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-mac/build/src/liballoc/rc.rs:755

另一个变体以非法指令退出。为什么会这样?我怎样才能用将被正确删除的值替换 self?

我还不确定如何实现你的目标,但是我可以证明

From my understanding, the usage of std::ptr::write should prevent the destructor from running

不正确:

use std::mem;
use std::ptr;

struct Noisy;
impl Drop for Noisy {
    fn drop(&mut self) { println!("Dropping!") }
}

enum Foo {
    Bar(Noisy),
    Baz
}
use Foo::*;

impl Drop for Foo {
    fn drop(&mut self) {
        println!("1");

        match self {
            &mut Bar(_) => {
                println!("2");
                unsafe { ptr::write(self, Foo::Baz) }
                println!("3");
            }
            &mut Baz => {
                println!("4");
            }
        }

        println!("5");
    }
}

fn main() {
    let _ = Foo::Bar(Noisy);
}

这会打印:

1
2
3
5
Dropping!

说明Foo::Bar的析构函数还在运行,包括Noisy的析构函数。

一个潜在的解决方案是使用 Option::take:

use std::mem;

struct Noisy;
impl Drop for Noisy {
    fn drop(&mut self) { println!("Dropping!") }
}

enum Foo {
    Bar(Option<Noisy>),
    Baz
}

impl Drop for Foo {
    fn drop(&mut self) {
        match *self {
            Foo::Bar(ref mut x) => {
                unsafe { mem::forget(x.take()) }
            }
            Foo::Baz => {}
        }
    }
}

fn main() {
    let _ = Foo::Bar(Some(Noisy));
}