The problem is that the lifetime of v is for the whole if/else block, even though it is not available in the else section. You can get around this by with the help of Option::cloned.
pub fn calc_deps(cache: &mut HashMap<String, String>, key: &String) -> String {
if let Some(v) = cache.get(key).cloned() {
v
} else {
let r = String::from("computations");
cache.insert(key.clone(), r.clone());
r
}
}
Option::cloned maps Option<&T> to Option<T> by cloning the contents. So now v becomes String instead of &String, and is no longer borrowing cache.
Another option is to use the HashMap::entry/or_insert_with interface. It's probably more idiomatic, but it requires unconditionally cloning the key.
pub fn calc_deps(cache: &mut HashMap<String, String>, key: String) -> String {
cache
.entry(key)
.or_insert_with(|| String::from("computations"))
.clone()
}
You could also simply use or_insert instead of or_insert_with, but that would require doing your computations for r unconditionally.
pub fn calc_deps(cache: &mut HashMap<String, String>, key: String) -> String {
cache
.entry(key)
.or_insert(String::from("computations"))
.clone()
}