But unfortunately the constructor of ExpensiveData is fallible (the data is loaded from a file). I can't use the try operator inside the lambda (.or_insert_with(|| faillible_operation()?) isn't valid for obvious reason).
It feels that my only solution is to do two look-up each time I want to access my data to solve this issue.
fn faillible_operation(key: u32) -> std::Result<ExpensiveData, SomeError> {
if rand() % 2 {
Err(SomeError)
} else {
Ok(key as ExpensiveData)
}
}
impl Cache {
fn get_value(&mut self, key: u32) -> std::Result<&ExpensiveData, SomeError> {
if cache.contains_key(&key) {
cache.get(&key).unwrap() // unrelated note: I don't understand why `cache[&3]` returns a value and not a reference
} else {
let expensive_data: ExpensiveData = faillible_operation(key)?; // I can use the try operator here
cache.entry(key).or_insert(expensive_data)
}
}
}
Just a small note, I was trying this out myself and this'll work but the Occupied branch needs to return e.into_mut() instead of e.get(), because get borrows from e which is owned by the function.
I guess it's a matter of opinion, but I don't see this as boilerplate. Entry has some nice methods for convenience, but it makes sense to require more work if you stray from those boundaries, like wanting to deal with your fallible operation.
Maybe there could be a new Entry method dealing with Result in particular:
I think this would be hard to actually use, because type inference can't tell that you want R1 and R2 to be the "same" Result type. They're not really the same since they have different type parameters, and I think we would need type constructors to make that really possible. So if type inference can't figure this out from surrounding context, you'll end up turbo-fishing these generic parameters.
I suspect the libs team would rather you just match the entry.