Search code examples
rustreferenceborrow-checker

How to return a reference with lifetime from RefCell?


For example, I have the next structure:

struct MyStruct {
    items: RefCell<HashMap<i32, SomeStruct>>,
}

impl MyStruct {
    fn new() -> Self {
        Self {
            items: RefCell::new(HashMap::new()),
        }
    }

    fn get_item(&self, key: &i32) -> Option<&SomeStruct> {
        let items = self.items.borrow();
        items.get(key)
    }
}

That code won't compile due to different lifetimes. So, I've tried to introduce these lifetimes:

    fn get_item<'a>(&'a self, key: &i32) -> Option<&'a SomeStruct> {
        let items = self.items.borrow();
        items.get(key)
    }

However it fixed a nothing.

Is it possible to return Option<&'a SomeStruct> or a similar type in this case?


Solution

  • As mentioned in the comments, you cannot return a named reference because such a reference would outlive the Ref returned by RefCell::borrow(). With the Ref gone, nothing would stop you from calling borrow_mut() to obtain a mutable reference with the shared reference still in existence, which would be undefined behavior.

    Instead, you can return an optional Ref which the caller can use to obtain a reference (whose lifetime is correctly tied to that Ref). To get Option<Ref<SomeStruct>>, you can use Ref::filter_map():

    fn get_item<'a>(&'a self, key: &i32) -> Option<Ref<'a, SomeStruct>> {
        Ref::filter_map(self.items.borrow(), |items| items.get(key)).ok()
    }
    

    Playground

    As mentioned in the comments, you can even hide RefCell usage from the public API, by returning Option<impl Deref<Target = SomeStruct> + 'a> instead. (Playground.)