I have seen several of these questions but couldn't find a solution to my problem yet.
The following caching function in Rust doesn't compile. It produces an error when calling insert
:
fn get_or_load_icon<'a>(icon_cache: &'a mut HashMap<String, TextureHandle>, icon_path: &str) -> &'a TextureHandle {
if let Some(texture_cache) = icon_cache.get(icon_path) {
texture_cache
} else {
let texture = load_image(icon_path);
icon_cache.insert(icon_path.to_string(), texture);
icon_cache.get(icon_path).unwrap()
}
}
error[E0502]: cannot borrow `*icon_cache` as mutable because it is also borrowed as immutable
--> src\main.rs:1108:3
|
1103 | fn get_or_load_icon<'a>(icon_cache: &'a mut HashMap<String, TextureHandle>, icon_path: &str) -> &'a TextureHandle {
| -- lifetime `'a` defined here
1104 | if let Some(texture_cache) = icon_cache.get(icon_path) {
| ------------------------- immutable borrow occurs here
1105 | texture_cache
| ------------- returning this value requires that `*icon_cache` is borrowed for `'a`
...
1108 | icon_cache.insert(icon_path.to_string(), texture);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
I understand that the immutable borrow is icon_cache.get
and the mutable borrow is icon_cache.insert
, but I don't understand why the immutable borrow is still active at that point, or if the lifetime influences anything. I thought the borrow would be dropped after the if let
block and be irrelevant in the else
.
It's also important to note that the same exact code outside of a function compiles just fine.
I know two solutions to this issue, neither of which is acceptable for me:
- Using
entry
, which clonesicon_path
even when it can easily be found in the HashMap from the&str
.
fn get_or_load_icon<'a>(icon_cache: &'a mut HashMap<String, TextureHandle>, icon_path: &str) -> &'a TextureHandle {
icon_cache.entry(icon_path.to_string()).or_insert_with(|| load_image(icon_path))
}
- Using
contains_key
andget
, which searches the HashMap twice.
fn get_or_load_icon<'a>(icon_cache: &'a mut HashMap<String, TextureHandle>, icon_path: &str) -> &'a TextureHandle {
if icon_cache.contains_key(icon_path) {
icon_cache.get(icon_path).unwrap()
} else {
let texture = load_image(icon_path);
icon_cache.insert(icon_path.to_string(), texture);
icon_cache.get(icon_path).unwrap()
}
}
Are there other options that could make this work without cloning icon_path or searching twice? Or am I doing something fundamentally wrong writing this function?