Reputation: 5071
I am trying to create a Cacher
struct which will store calculated values in a HashMap
. The calculation
method will take one variable of type T
, do calculations and return a value with the same type T
. The type for this calculation
callback will be Fn(T) -> T
.
I figured out that value which will be a key of HashMap
has to implement the Eq
and Hash
traits. It looked like everything should be working and I could compile my program without errors.
Then I wrote one test to check everything works as expected:
use std::{hash::Hash, collections::HashMap};
struct Cacher<T, U>
where
T: Fn(U) -> U,
{
calculation: T,
values: HashMap<U, U>,
}
impl<T, U> Cacher<T, U>
where
T: Fn(U) -> U,
U: Eq + Hash + Clone,
{
fn new(calculation: T) -> Cacher<T, U> {
return Cacher {
calculation,
values: HashMap::new(),
};
}
fn value(&mut self, arg: U) -> U {
let result = self.values.get(&arg);
return match result {
Some(v) => v.clone(),
None => self
.values
.insert(arg.clone(), (self.calculation)(arg.clone()))
.unwrap_or_else(|| {
panic!("Unexpected error occurred");
})
.clone(),
};
}
}
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a: i32| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 2);
}
thread 'call_with_different_values' panicked at 'Unexpected error occurred', src/lib.rs:31:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
When I insert a new value into my HashMap
, it always finishes with Option::None
, my unwrap_or_else
callback is called, and then I throw an error. What am I doing wrong?
Upvotes: 0
Views: 797
Reputation: 35482
Your error comes from the fact that insert
returns an Option
with the previous value at the key, not the new one. Instead, use Entry
:
fn value(&mut self, arg: U) -> U {
// ugly workaround to borrow checker complaining when writing these inline
let value_ref = &mut self.values;
let calculation_ref = &self.calculation;
let result = value_ref.get(&arg);
self.values.entry(arg.clone())
.or_insert_with(|| (calculation_ref)(arg.clone()))
.clone()
}
Upvotes: 5