为什么在 `Cell<T>` 上定义的 `set` 方法显式删除旧值?(锈)

New*_*ewb 14 memory cell ownership rust borrow-checker

感兴趣为什么在最后一行set定义的方法显式地删除值。当函数返回时,它不应该被隐式删除(释放内存)吗?Cellold

use std::mem;
use std::cell::UnsafeCell;

pub struct Cell<T> {
    value: UnsafeCell<T>
}

impl<T> Cell<T> {
    pub fn set(&self, val: T) {
        let old = self.replace(val);
        drop(old); // Is this needed?
    } // old would drop here anyways?
    
    pub fn replace(&self, val: T) -> T {
        mem::replace(unsafe { &mut *self.value.get() }, val)
    }
}
Run Code Online (Sandbox Code Playgroud)

那么为什么不设置只这样做:

pub fn set(&self, val: T) {
    self.replace(val);
}
Run Code Online (Sandbox Code Playgroud)

或者std::ptr::read做了一些我不明白的事情。

Loc*_*cke 10

这不是必需的,但在某些情况下drop显式调用可以帮助使代码更易于阅读。如果我们只将其编写为对 的调用,那么它看起来就像是 的包装函数,并且读者可能会丢失它在调用方法之上执行附加操作(删除先前值)的上下文。归根结底,使用哪个版本有些主观,并且在功能上没有区别。replacereplacereplace

话虽这么说,真正的原因是它在设置时并不总是删除以前的值。Cell<T>以前set通过不安全的指针操作实现覆盖现有值。后来在 rust-lang/rust#39264 中对其进行了修改:将 Cell 扩展到非复制类型,以便始终删除先前的值。作者 (wesleywiser) 可能想要更明确地表明,当将新值写入单元格时,先前的值将被删除,以便更容易审查拉取请求。

就我个人而言,我认为这是一个很好的用法,drop因为它有助于传达我们打算对该replace方法的结果执行的操作。

  • 此外,在调试版本中,如果出现紧急情况,拥有两个不同的源代码行号可能有助于查明原因。虽然我不认为“替换”会引起恐慌。 (2认同)