Generics 是否可以对HashMap的键和值使用一个泛型?

Generics 是否可以对HashMap的键和值使用一个泛型?,generics,rust,closures,Generics,Rust,Closures,在中,您实现了一个缓存器,以使用memonization演示函数式编程以及如何加速长时间运行的任务。作为一个额外的挑战,他们建议让缓存器使用哈希映射允许多个键,并利用泛型实现更大的灵活性 尝试修改缓存以保存哈希映射,而不是单个值。 散列映射的键将是传入的arg值, 哈希映射的值将是调用 关上那把钥匙。而不是直接查看self.value 如果有Some或None值,则value函数将在中查找arg 哈希映射并返回值(如果存在)。如果不是 此时,缓存器将调用闭包并保存结果值 在与其arg值关联的哈希

在中,您实现了一个
缓存器
,以使用memonization演示函数式编程以及如何加速长时间运行的任务。作为一个额外的挑战,他们建议让
缓存器
使用
哈希映射允许多个键
,并利用泛型实现更大的灵活性

尝试修改
缓存
以保存哈希映射,而不是单个值。 散列映射的键将是传入的
arg
值, 哈希映射的值将是调用 关上那把钥匙。而不是直接查看
self.value
如果有
Some
None
值,则value函数将在中查找
arg
哈希映射并返回值(如果存在)。如果不是 此时,
缓存器将调用闭包并保存结果值
在与其
arg
值关联的哈希映射中

当前的
缓存实现的第二个问题是
仅接受接受接受一个类型为
u32
的参数并返回
u32
。我们可能希望缓存接受字符串的闭包的结果 例如,切片并返回
usize
值。要解决此问题,请尝试 引入更多通用参数以增加
缓存器
功能

我能够实现
HashMap
,但是当试图用泛型类型替换闭包定义
u32
,并将其用作
HashMap
的签名时,我遇到了一个问题

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);
}
导致以下错误:

错误:应为“!”之一`(`,`+`,`::`,`,`,找到`:`
-->src/main.rs:16:39
|

16 | impl Cacher您的实现无法编译,因为只有在
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(),
        }
    }
}

请注意,在这个解决方案中,我添加了一个约束,即
K
Clone

这个问题很老了,但我在网上浏览时发现了这个问题,以查看我的解决方案是否“正确”我将在这里分享它,因为它与公认的答案略有不同,并保留了书中
方法的原始签名:

struct Cacher<T, K, V>
    where T: Fn(K) -> V,
          K: Eq + Hash + Copy,
          V: Copy {
    calculation: T,
    cache: HashMap<K, V>,
}

impl<T, K, V> Cacher<T, K, V>
    where T: Fn(K) -> V,
          K: Eq + Hash + Copy,
          V: Copy {
    fn new(calculation: T) -> Cacher<T, K, V> {
        Cacher {
            calculation,
            cache: HashMap::new(),
        }
    }

    fn value(&mut self, arg: K) -> V {
        match self.cache.get(&arg) {
            Some(&v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.cache.insert(arg, v);
                v
            }
        }
    }
}
结构缓存器 其中T:Fn(K)->V, K:Eq+Hash+Copy, V:复制{ 计算:T, 缓存:HashMap, } 嵌入式缓存器 其中T:Fn(K)->V, K:Eq+Hash+Copy, V:复制{ fn新(计算:T)->缓存{ 缓存器{ 计算, 缓存:HashMap::new(), } } fn值(&mut self,arg:K)->V{ 匹配self.cache.get(&arg){ 一些(&v)=>v, 无=>{ 设v=(自计算)(arg); self.cache.insert(arg,v); v } } } }

这里我为HashMap的键和值提供了显式的通用参数,它将缓存
Cacher
的值
K
具有
Eq
Hash
Copy
,并且
V
只需要
Copy
特征绑定。

你问的问题不清楚,你想要修复你的第一个被剪断的部分还是你的第二个?@Shepmaster是否真的有必要在编辑中按字母顺序重新排列导入?这是生锈的首选做法还是你个人的偏好?@MikeS。我将所有代码复制到这里,粘贴到中,运行,然后复制回。这就是为什么我的修订评论说“标准生锈缩进和样式”我这样做是为了让所有的问答都有一个一致的风格,这使得每个看过这些帖子的人不必为了得到答案而适应每个人的个人风格。事实上,我个人不同意Rustfmt所做的一些改变。谢谢你提供的信息。这让有道理。我还没有听说过Rustfmt,所以我会检查一下。谢谢,我会尝试一下并报告。通过添加克隆,我假设您不需要使用生命周期并通过引用传递。关于使用克隆的性能有什么想法吗?或者这是可接受的做法,影响可以忽略不计吗?在第一次尝试中(我在相信之前尝试过)我在闭包中遇到了一个终生问题…``-->src/main.rs:40:10 | 40 |&num | ^^^借用值的寿命不够长41 |}
num
在仍然借用的情况下删除了此处``我将尝试省略生命周期并使用克隆…嗨,迈克,尝试一下,谢谢!
Clone
方法可以编译并工作。最新的方法也有效。接受答案,但我会编辑最上面的部分,并将您最新的代码片段放入其中,以利于其他人。建议使用哪种方法(使用生存期并省略克隆,或者简化语法并使用克隆)?我个人更喜欢hashmap拥有值的解决方案,因为对我来说听起来更习惯,但这是我的观点。
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)),
        }
    }
}
struct Cacher<T, K, V>
    where T: Fn(K) -> V,
          K: Eq + Hash + Copy,
          V: Copy {
    calculation: T,
    cache: HashMap<K, V>,
}

impl<T, K, V> Cacher<T, K, V>
    where T: Fn(K) -> V,
          K: Eq + Hash + Copy,
          V: Copy {
    fn new(calculation: T) -> Cacher<T, K, V> {
        Cacher {
            calculation,
            cache: HashMap::new(),
        }
    }

    fn value(&mut self, arg: K) -> V {
        match self.cache.get(&arg) {
            Some(&v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.cache.insert(arg, v);
                v
            }
        }
    }
}