尽管值已经存在,条目:: or_insert仍会执行

时间:2020-02-07 08:41:44

标签: rust

chapter 13 of the Rust book中,您实现了Cacher结构用于延迟初始化,以演示闭包和函数式编程的用法。作为一种练习,他们鼓励读者尝试创建通用的Cacher,它可以存储多个值。为此,他们建议使用Hashmap

尝试修改Cacher以保留哈希图,而不是单个值。哈希图的键将是传入的arg值,哈希图的值将是对该键调用闭包的结果。而不是查看self.value是否直接具有Some或None值,value函数将在哈希图中查找arg并返回该值(如果存在)。如果不存在,Cacher会调用该闭包,并将结果值保存在与其arg值关联的哈希图中。

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

为解决此问题,我使用了以下代码:

struct Cacher<T, K, V>
    where T: Fn(K) -> V
{
    calculation: T,
    values: HashMap<K, V>,
}

impl<T, K, V> Cacher<T, K, V>
    where T: Fn(K) -> V,
          K: std::cmp::Eq + std::hash::Hash + Clone,
{
    fn new(calculation: T) -> Cacher<T, K, V> {
        Cacher {
            calculation,
            values: HashMap::new(),
        }
    }

    fn value(&mut self, intensity: K) -> &V {
        self.values.entry(intensity.clone()).or_insert((self.calculation)(intensity))
    }
}

该代码可以编译并运行,但由于始终执行Cacher的事实,因此不能用作正确的(self.calculation)(intensity)。即使该条目存在。我从文档和示例中了解到,Entry::or_insert函数仅在Entry不存在时才执行。

我知道问题Is it possible to use a single generic for both key and value of a HashMap?的解决方案,但是我想知道是否有可能以目前的方式解决问题。

编辑:如注释中所述:or_insert_with无法解决问题。尝试or_insert_with(|| (self.calculation)(intensity.clone()))时,出现以下错误error[E0502]: cannot borrow self as immutable because it is also borrowed as mutable

1 个答案:

答案 0 :(得分:7)

您的代码的问题在于,总是在用Rust(以及大多数命令式语言)调用函数之前评估函数参数。这意味着甚至在调用or_insert()之前,代码将无条件调用(self.calculation)(intensity)or_insert()函数将在内部检查条目中是否已经存在一个值,并且仅插入一个新值(如果不存在则作为参数传递),但这仅在之后 {{ 1}}已被调用。

可以使用self.calculation方法解决此问题。此方法接受闭包而不是值,并且仅在需要插入值时才调用闭包。这是完整的代码:

or_insert_with()

实现use std::collections::HashMap; struct Cacher<T, K, V> { calculation: T, values: HashMap<K, V>, } impl<T, K, V> Cacher<T, K, V> where K: std::cmp::Eq + std::hash::Hash + Clone, { fn new(calculation: T) -> Cacher<T, K, V> { Cacher { calculation, values: HashMap::new(), } } fn value(&mut self, intensity: K) -> &V where T: Fn(K) -> V, { let calculation = &self.calculation; self.values .entry(intensity.clone()) .or_insert_with(|| calculation(intensity)) } } 的一个妙处是,您需要将对value()的引用存储在单独的变量中。否则,闭包将触发借用self.calculation,这与调用self触发的可变借用self.values重叠。如果您仅在外部范围中显式借用self.values.entry(),则借用检查器非常聪明,可以确定它与self.calculation不重叠。

作为旁注,我建议使用self.values来实现一致的代码格式。我还建议尽可能缩小特征范围的范围,以避免不必要的重复。这两个建议都包含在上面的代码中。

相关问题