How to implement a cache or by-need pattern?

Hi all,

Consider a struct with a hash map or tree map. I am trying to implement a function where values are inserted into the map as needed. A snippet goes as follows:

struct MyStruct {
  map: BTreeMap<String, String>,
}

impl MyStruct {
  fn ensure<F: FnOnce() -> String>(&mut self, k: &String, f: F) -> &String {
    match self.get(k) {
      Some(r) => r,
      None => {
        self.map.insert(k, f());
        self.map.get(k).unwrap()
      }
  }
}

fn go(mystruct: &mut MyStruct, k: String) -> &String {
  mystruct.ensure(k, || some_computation_with_mut_MyStruct(mystruct))
}

The borrow checker doesn't like it and I understand why. But how can code pattern like this can possibly work?

You probably want to use the entry API. As is, your call to get creates a shared borrow over self, so it will be unavailable for modification.

So you might use map.entry(k).or_insert_with(f) for example.

thanks for you fast response. I think I probably didn't make it clear. I didn't want to modify the return reference but the map itself.

Here the compiler complains that in

  mystruct.ensure(k, || some_computation_with_mut_MyStruct(mystruct))

mutable references of mystruct are borrowed twice. essentially my goal is to update the map while holding a mutable reference. Is that possible?
`

Ah yes, I misunderstood.

The problem here is similar in that you're creating a shared borrow over mystruct and trying to pass it a unique borrow over mystruct, and that can't be done because there's no way for both of those to safely exist at the same time.

What you can do is borrow parts of your struct separately rather than borrowing the whole thing, but this may mean you won't be able to use method syntax without creating some sort of additional wrapper type.

Another option is using interior mutability, but that usually has some overhead. Which is better probably depends on what you're trying to do.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.