`Entry::or_insert_with_key`

I am trying to create a struct that memoizes a given function. However, I ended up needing to write code like this:

match self.cache.borrow_mut().entry(key) {
    Entry::Occupied(e) => e.into_mut(),
    Entry::Vacant(e) => {
        let value = (self.transform)(e.key());
        e.insert(value)
    }
}

Ideally I would be able to use std::collections::hash_map::Entry::or_insert_with like this:

let entry = self.cache.borrow_mut().entry(key);
entry.or_insert_with(|| (self.transform)(entry.key()));

However this doesn't compile because by the time we're in the closure, we've moved key into entry(), then moved entry into or_insert_with() so it's not available for the closure to use.

I propose adding a new method to fix this: or_insert_with_key<F: FnOnce(&K) -> V>(self, default: F) -> &'a mut V. It works exactly the same as the or_insert_with but gives the closure a reference to the key, similarly to the first block of code above. In fact adding this would compact it to:

self.cache.borrow_mut().entry(key).or_insert_with_key(&self.transform);

Here's a preliminary working implementation:

fn or_insert_with_key<'a, F: FnOnce(&K) -> V, K, V>(
    entry: Entry<'a, K, V>,
    default: F,
) -> &'a mut V {
    match entry {
        Entry::Occupied(e) => e.into_mut(),
        Entry::Vacant(e) => {
            let value = default(e.key());
            e.insert(value)
        }
    }
}

// ...

or_insert_with_key(
    self.cache.borrow_mut().entry(key),
    &self.transform,
)
1 Like