rust - 尽管值已经存在,但 Entry::or_insert 仍然执行
问题描述
在Rust 书的第 13 章中,您实现了一个Cacher
用于延迟初始化的结构,以演示闭包和函数式编程的使用。作为练习,他们鼓励读者尝试创建一个Cacher
可以存储多个值的泛型。为此,他们建议使用Hashmap
.
尝试修改缓存器以保存哈希映射而不是单个值。哈希映射的键将是传入的 arg 值,哈希映射的值将是对该键调用闭包的结果。value 函数不会直接查看 self.value 是否具有 Some 或 None 值,而是会在哈希映射中查找 arg 并返回该值(如果存在)。如果它不存在,缓存器将调用闭包并将结果值保存在与其 arg 值关联的哈希映射中。
当前 Cacher 实现的第二个问题是它只接受带有一个 u32 类型参数并返回 u32 的闭包。例如,我们可能希望缓存采用字符串切片并返回 usize 值的闭包结果。要解决此问题,请尝试引入更多通用参数以增加缓存功能的灵活性。
为了解决这个练习,我使用了以下代码:
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
我知道问题练习的解决方案是否可以对 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
。
解决方案
您的代码的问题是函数参数总是在调用 Rust(和大多数命令式语言)中的函数之前进行评估。这意味着在or_insert()
调用之前,代码将无条件调用(self.calculation)(intensity)
. 该or_insert()
函数将在内部检查一个值是否已经存在于条目中,如果没有,则仅插入它作为参数传递的新值,但这仅在 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
作为旁注,我建议使用rustfmt
一致的代码格式。我还建议尽可能缩小特征范围,以避免不必要的重复。这两个建议都包含在上面的代码中。
推荐阅读
- python-3.x - 使用 BioPython 将 FASTA seq_ID 替换为 dict 中的新 ID
- android - Flutter/cloud-firestore“任务已经完成”异常
- javascript - RegExp 后向断言替代方案
- typescript - 在 snapshot.forEach 循环中检索数据
- python - 如何获得相关矩阵值pyspark
- sql - 如何处理 CASE WHEN SQL 语句中不存在的记录?
- c++ - 在 Visual Studio 2017 的 CMake 项目中使用库
- java-8 - 在并行流中窥视以增加计数器
- git - git合并时重复代码(自动合并)
- xcode - Xcode 集成需要防火墙端口转发?