This question already has an answer here:
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!"));
}
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 theRefCell
's. That's because the reference's lifetime is restricted by the guard returned byborrow()
. 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 withRefCell
— if you don't need to do that, then ditch theRefCell
and write&mut self
on the methods that mutate the map), you could accidentally destroy aString
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 theRc<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.