要考虑的另一种方法可能是按值进行缓存: struct Cacher< T,K,V>在哪里T:Fn(K)→V,{计算:T,值:HashMap< K,V> ;,}impl< T,K,V>Cacher T,K,V = 1.在哪里T:Fn(K)→V,K:哈希+均衡+克隆{fn new(计算:T)->Cacher T,K,V = 1.{Cacher {计算,值:HashMap :: new(),}}fn值(& mut self,arg:K)->& V {匹配self.value.entry(arg.clone()){Entry :: Occupied(v)=>v.into_mut(),Entry :: Vacant(v)=>v.insert((self.calculation)(arg)),}}} 请注意,在此解决方案中,我添加了 K 为 Clone 的约束.In chapter 13 of the Rust book, you implement a Cacher to use memoization to demonstrate functional programming and how to speed up long-running tasks. As an extra challenge, they recommend making the Cacher allow multiple keys using a HashMap and also leveraging generics to allow more flexibility. I was able to implement the HashMap, however when trying to replace the closure definition u32 with a generic type and use that as the signature of the HashMap, I run into an issue.use std::collections::hash_map::Entry;use std::collections::HashMap;use std::thread;use std::time::Duration;struct Cacher<'a, T>where T: Fn(&'a u32) -> &'a u32,{ calculation: T, values: HashMap<&'a u32, &'a u32>,}impl<'a, T> Cacher<'a, T>where T: Fn(&'a u32) -> &'a u32,{ fn new(calculation: T) -> Cacher<'a, T> { Cacher { calculation, values: HashMap::new(), } } fn values(&mut self, arg: &'a u32) -> &'a u32 { match self.values.entry(arg) { Entry::Occupied(e) => &*e.into_mut(), Entry::Vacant(e) => &*e.insert(&(self.calculation)(&arg)), } }}fn generate_workout(intensity: u32, random_number: u32) { let mut expensive_result = Cacher::new(|num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); &num }); if intensity < 25 { println!("Today, do {} pushups!", expensive_result.values(&intensity)); println!("Next, do {} situps!", expensive_result.values(&intensity)); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!( "Today, run for {} minutes!", expensive_result.values(&intensity) ); } }}fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number);}I tried K, V generics as below and it complains with Expected one of 7 possible values here pointing to the first type definition.use std::collections::hash_map::Entry;use std::collections::HashMap;use std::hash::Hash;use std::thread;use std::time::Duration;struct Cacher<'a, T: 'a, K: 'a, V: 'a>where T: Fn(&'a K) -> &'a V, K: Hash + Eq,{ calculation: T, values: HashMap<&'a K, &'a V>,}impl<'a, T: 'a, K: 'a, V: 'a> Cacher<'a, T: 'a, K: 'a, V: 'a>where T: Fn(&'a K) -> &'a V, K: Hash + Eq,{ fn new(calculation: T) -> Cacher<'a, T: 'a, K: 'a, V: 'a> { Cacher { calculation, values: HashMap::new(), } } fn values(&mut self, arg: &'a K) -> &'a V { match self.values.entry(arg) { Entry::Occupied(e) => &*e.into_mut(), Entry::Vacant(e) => &*e.insert(&(self.calculation)(&arg)), } }}fn generate_workout(intensity: u32, random_number: u32) { let mut expensive_result = Cacher::new(|num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); &num }); if intensity < 25 { println!("Today, do {} pushups!", expensive_result.values(&intensity)); println!("Next, do {} situps!", expensive_result.values(&intensity)); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!( "Today, run for {} minutes!", expensive_result.values(&intensity) ); } }}fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number);}Results in the following error:error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `:` --> src/main.rs:16:39 |16 | impl<'a, T: 'a, K: 'a, V: 'a> Cacher<T: 'a, K: 'a, V: 'a> | ^ expected one of 7 possible tokens hereIs the only way to add 2 more generics (i.e. K, V) or is there a way to reuse a single generic? If 2 required, what am I missing above?Is there a more idiomatic approach to solving this problem? The Rust book does not offer a solution, unfortunately. 解决方案 Your implementation does not compile because lifetime bounds have to be declared only after impl:impl<'a, T: 'a, K: 'a, V: 'a> Cacher<'a, T, K, V>where T: Fn(&'a K) -> &'a V, K: Hash + Eq,{ fn new(calculation: T) -> Cacher<'a, T, K, V> { Cacher { calculation, values: HashMap::new(), } }}Storing references into the HashMap implies that you have to manage lifetimes and assure that the values referenced by HashMap outlive the Cacher.Another approach to consider may be to cache by values:struct Cacher<T, K, V>where T: Fn(K) -> V,{ calculation: T, value: HashMap<K, V>,}impl<T, K, V> Cacher<T, K, V>where T: Fn(K) -> V, K: Hash + Eq + Clone{ fn new(calculation: T) -> Cacher<T, K, V> { Cacher { calculation, value: HashMap::new(), } } fn value(& mut self, arg: K) -> &V { match self.value.entry(arg.clone()) { Entry::Occupied(v) => v.into_mut(), Entry::Vacant(v) => v.insert((self.calculation)(arg)), } }}Please note that in this solution I added the constraint that K is Clone 这篇关于是否可以对HashMap的键和值使用单个泛型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
06-09 19:18