如何获取 Rust 中 std::cell::Cell 内部数据的不可变引用?

3

查看 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);
}

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

谢谢你的帮助!

2个回答

9
首先,如果你有一个 &mut T,那么你可以轻松地从中获取一个 &T。所以你可以使用 get_mut 来获取 &T
但是要从 Cell<T> 中获取一个 &mut T,你需要将该单元格设置为可变的,因为 get_mut 需要传入一个 &mut self 参数。而这正是设计时获取单元格内部对象引用的唯一方式。
通过要求使用 &mut self 方法来从单元格中获取引用,你使得借用检查器可以在编译时检查独占访问的情况。记住,单元格实现了内部可变性,并且具有 set(&self, val: T) 方法,即修改非 mut 绑定值的方法!如果存在 get(&self) -> &T 方法,则借用检查器无法确保在设置对象时您不持有对内部对象的引用,这是不安全的。
总之,按照设计,你无法从非 mutCell<T> 中获取一个 &T。使用 get_mut(需要一个 mut 单元格)或者 set/replace(适用于非 mut 单元格)。如果这不可接受,那么请考虑使用 RefCell,它可以为您获取一个非 mut 实例的 &T,但是会带来一些运行时开销。

4
除了@mcarton的回答之外,为了保持内部可变性的完整性,即不允许可变引用与其他引用共存,我们有三种不同的方法:
  1. 使用unsafe可能导致未定义行为。这就是UnsafeCell的作用。
  2. 需要一些运行时检查,会带来运行时开销。这是RefCellRwLockMutex所采用的方法。
  3. 限制可以使用抽象的操作。这是CellAtomic*和(不稳定的)OnceCell(因此使用它的Lazy)所采用的方法(请注意,线程安全类型也有运行时开销,因为它们需要提供某种形式的锁定)。每个类型都提供了不同的允许操作集:
    • CellAtomic*不允许您获取包含值的引用,并且只能整体替换它(基本上是get()set,尽管在这些操作之上还提供了方便的方法,例如swap())。对于Cell,还可以进行投影(从单元格到切片的投影),虽然字段投影是可能的,但不作为std的一部分提供。
    • OnceCell只允许您分配一次,并且只能在此之后获取共享引用,确保在分配时没有引用,并且在拥有共享引用时不能再分配。
因此,当您需要将引用带入内容中时,您不能选择Cell,因为它不是为此而设计的 - 显然的选择是RefCell

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