Is there a way to get around the extra .borrow() call here? Something kinda like the HashMap entry API?
I tried different variations, but kept coming back to either not being able to get a Ref from a RefMut or borrowing while the mutable borrow is still alive
struct Foo {
pub ref_cell: RefCell<Option<String>>
}
impl Foo {
fn get(&self) -> Ref<'_, String> {
// if it's None, set it
if self.ref_cell.borrow().is_none() {
*self.ref_cell.borrow_mut() = Some(String::from("hello world"));
}
// either way, get it
Ref::map(self.ref_cell.borrow(), |inner| inner.as_ref().unwrap())
}
}
I don't think a RefCell borrow costs much at all. Sure it's not free because it's checking the status, but unlike a HashMap's Entry that needs to perform a hash and search, RefCell's borrow is just checking a fixed structure.
you mean like this? It does help a bit, thanks, but there's still the problem of the extra borrow in case of Some
impl Foo {
fn get(&self) -> Ref<'_, String> {
// if it's None, set it
{
let mut lock = self.ref_cell.borrow_mut();
if lock.as_ref().is_none() {
*lock = Some(String::from("hello world"));
}
}
// either way, get it
Ref::map(self.ref_cell.borrow(), |inner| inner.as_ref().unwrap())
}
}
You can drop it either by creating a new scope that ends before the .borrow() call:
{
let mut borrow = self.ref_cell.borrow_mut();
if borrow.is_none() {
*borrow = Some(String::from("hello world"));
}
}
Ref::map(self.ref_cell.borrow(), |inner| inner.as_ref().unwrap())
Or by ending explicitly dropping the RefMut before taking a shared borrow.
let mut borrow = self.ref_cell.borrow_mut();
if borrow.is_none() {
*borrow = Some(String::from("hello world"));
}
drop(borrow);
Ref::map(self.ref_cell.borrow(), |inner| inner.as_ref().unwrap())
The reason your current version works is that the immutable borrow is never assigned to a variable, so its lifetime ends as soon as the if condition is evaluated.
The optimizer shouldn't have any trouble figuring out what's going on here. If you have any doubts, you can check the generated assembly for the two version (using release mode) at the rust playground.