I have a type that stores its data in a container behind a Rc<RefCell<>>
, which is for the most part hidden from the public API. For example:
struct Value;
struct Container {
storage: Rc<RefCell<HashMap<u32, Value>>>,
}
impl Container {
fn insert(&mut self, key: u32, value: Value) {
self.storage.borrow_mut().insert(key, value);
}
fn remove(&mut self, key: u32) -> Option<Value> {
self.storage.borrow_mut().remove(&key)
}
// ...
}
However, peeking inside the container requires returning a Ref
. That can be achieved using Ref::map()
- for example:
// peek value under key, panicking if not present
fn peek_assert(&self, key: u32) -> Ref<'_, Value> {
Ref::map(self.storage.borrow(), |storage| storage.get(&key).unwrap())
}
However, I'd like to have a non-panicking version of peek
, that would return Option<Ref<'_, Value>>
. This is a problem because Ref::map
requires that you return a reference to something that exists inside the RefCell
, so even if I wanted to return Ref<'_, Option<Value>>
, it wouldn't work because the option returned by storage.get()
is ephemeral.
Trying to use Ref::map
to create a Ref
from a previously looked up key doesn't compile either:
// doesn't compile apparently the borrow checker doesn't understand that `v`
// won't outlive `_storage`.
fn peek(&self, key: u32) -> Option<Ref<'_, Value>> {
let storage = self.storage.borrow();
if let Some(v) = storage.get(&key) {
Some(Ref::map(storage, |_storage| v))
} else {
None
}
}
The approach that does work is to perform the lookup twice, but that's something I'd really like to avoid:
// works, but does lookup 2x
fn peek(&self, key: u32) -> Option<Ref<'_, Value>> {
if self.storage.borrow().get(&key).is_some() {
Some(Ref::map(self.storage.borrow(), |storage| {
storage.get(&key).unwrap()
}))
} else {
None
}
}
Compilable example in the playground.
Related questions like this one assume that the inner reference is always available, so they don't have that issue.
I found Ref::filter_map()
which would solve this, but it's not yet available on stable, and it's unclear how far it is from stabilization. Barring other options, I would accept a solution that uses unsafe
provided it is sound and relies on documented guarantees.