Search code examples
pointersvariablesrustreferenceunsafe

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


Looking through the documentation for std::cell::Cell, I don't see anywhere how I can retrieve a non-mutable reference to inner data. There is only the get_mut method: https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get_mut

I don't want to use this function because I want to have &self instead of &self mut.

I found an alternative solution of taking the raw pointer:

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

Is there any way to do this without using unsafe? If not, perhaps RefCell will be more practical here?

Thank you for help!


Solution

  • First of, if you have a &mut T, you can trivially get a &T out of it. So you can use get_mut to get &T.

    But to get a &mut T from a Cell<T> you need that cell to be mutable, as get_mut takes a &mut self parameter. And this is by design the only way to get a reference to the inner object of a cell.

    By requiring the use of a &mut self method to get a reference out of a cell, you make it possible to check for exclusive access at compile time with the borrow checker. Remember that a cell enables interior mutability, and has a method set(&self, val: T), that is, a method that can modify the value of a non-mut binding! If there was a get(&self) -> &T method, the borrow checker could not ensure that you do not hold a reference to the inner object while setting the object, which would not be safe.


    TL;DR: By design, you can't get a &T out of a non-mut Cell<T>. Use get_mut (which requires a mut cell), or set/replace (which work on a non-mut cell). If this is not acceptable, then consider using RefCell, which can get you a &T out of a non-mut instance, at some runtime cost.