I have a RefCell<HashMap>
and want to borrow the table, find a key, and return a reference to the result:
use std::cell::RefCell;
use std::collections::HashMap;
struct Frame {
map: RefCell<HashMap<String, String>>,
}
impl Frame {
fn new() -> Frame {
Frame {
map: RefCell::new(HashMap::new()),
}
}
fn lookup<'a>(&'a self, k: &String) -> Option<&'a String> {
self.map.borrow().get(k)
}
}
fn main() {
let f = Frame::new();
println!("{}", f.lookup(&"hello".to_string()).expect("blargh!"));
}
(playground)
If I remove the RefCell
then everything works okay:
struct Frame {
map: HashMap<String, String>,
}
impl Frame {
fn lookup<'a>(&'a self, k: &String) -> Option<&'a String> {
self.map.get(k)
}
}
What is the correct way to write the lookup function without copying the string in the hashtable?
When you borrow from a RefCell
, the reference you get has a shorter lifetime than the RefCell
's. That's because the reference's lifetime is restricted by the guard returned by borrow()
. That guard ensures that nobody else can take a mutable reference to the value until the guard is dropped.
However, you are trying to return a value without keeping a guard alive. If Frame
had a method that took a &self
argument but tried to mutate the map (which is possible with RefCell
— if you don't need to do that, then ditch the RefCell
and write &mut self
on the methods that mutate the map), you could accidentally destroy a String
that somebody else has a reference to. That is exactly the kind of errors that the borrow checker was designed to report!
If the map values are effectively immutable (i.e. your type will not allow mutating the map's values), you could also wrap them in an Rc
in your map. You could therefore return a clone of the Rc<String>
(this only clones the reference-counted pointer, not the underlying string), which would let you release the borrow on the map before returning from the function.
struct Frame {
map: RefCell<HashMap<String, Rc<String>>>
}
impl Frame {
fn lookup(&self, k: &String) -> Option<Rc<String>> {
self.map.borrow().get(k).map(|x| x.clone())
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With