Rust,std::cell::Cell - 获取对内部数据的不可变引用

Rust, std::cell::Cell - get immutable reference to inner data

查看 std::cell::Cell 的文档,我在任何地方都看不到如何检索对内部数据的非可变引用。只有get_mut方法:https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get_mut

我不想使用这个函数,因为我想要 &self 而不是 &self mut

我找到了另一种采用原始指针的解决方案:

use std::cell::Cell;

struct DbObject {
    key: Cell<String>,
    data: String
}

impl DbObject {
    pub fn new(data: String) -> Self {
        Self {
            key: Cell::new("some_uuid".into()),
            data,
        }
    }

    pub fn assert_key(&self) -> &str {
        // setup key in the future if is empty...
        let key = self.key.as_ptr();
        unsafe {
            let inner = key.as_ref().unwrap();
            return inner;
        }
    }
}

fn main() {
    let obj = DbObject::new("some data...".into());
    let key = obj.assert_key();
    println!("Key: {}", key);
}

有没有不使用不安全的方法来做到这一点?如果没有,也许 RefCell 在这里会更实用?

感谢您的帮助!

首先,如果你有一个 &mut T,你可以很容易地从中得到一个 &T。所以你可以使用 get_mut 得到 &T.

但是要从 Cell<T> 中获取 &mut T,您需要该单元格是可变的,因为 get_mut 需要一个 &mut self 参数。这是通过设计获得对单元格内部对象的引用的唯一方法。

通过要求使用 &mut self 方法从单元格中获取引用,您可以在编译时使用借用检查器检查独占访问。请记住,一个单元格启用内部可变性,并且有一个方法 set(&self, val: T),即可以修改非 mut 绑定的值的方法!如果有 get(&self) -> &T 方法,借用检查器无法确保您在设置对象时不持有对内部对象的引用,这是不安全的。


TL;DR:按照设计,您无法从非 mut Cell<T> 中得到 &T。使用 get_mut(需要 mut 单元格)或 set/replace(适用于非 mut 单元格)。如果这是不可接受的,请考虑使用 RefCell,它可以让你从非 mut 实例中获得 &T,但需要一些运行时成本。

除了@mcarton的回答,为了保持内部可变性的合理性,即不允许可变引用与其他引用共存,我们有三种不同的方式:

  1. 使用 unsafe 可能会出现未定义的行为。这就是 UnsafeCell 所做的。
  2. 进行一些运行时检查,涉及运行时开销。这是 RefCellRwLockMutex 使用的方法。
  3. 限制抽象可以完成的操作。这就是使用它的 CellAtomic* 和(不稳定的)OnceCell (and thus Lazy 所做的(注意线程安全类型也有运行时开销,因为它们需要提供某种锁定).每个都提供一组不同的允许操作:
    • CellAtomic* 不会让您获得对包含值的引用,只能将其整体替换(基本上,get() and set, though convenience methods are provided on top of these, such as swap())。投影(细胞切片到细胞切片)也可用于 Cell(场投影是可能的,但不作为标准的一部分提供)。
    • OnceCell 允许您仅分配一次,然后才采用共享引用,保证在您分配时没有引用,并且当您有共享引用时您不能再分配。

因此,当您需要能够引用内容时,您不能选择 Cell,因为它不是为此而设计的 - 显而易见的选择是 RefCell,的确。