Rust Entry::or_insert执行,尽管值已存在
在中,您实现了一个用于延迟初始化的Rust Entry::or_insert执行,尽管值已存在,rust,Rust,在中,您实现了一个用于延迟初始化的Cacherstruct,以演示闭包和函数编程的使用。作为练习,他们鼓励读者尝试创建一个通用的缓存,它可以存储多个值。为此,他们建议使用Hashmap 尝试修改缓存以保存哈希映射,而不是单个值。哈希映射的键将是传入的arg值,哈希映射的值将是对该键调用闭包的结果。value函数不会直接查看self.value是否有Some或None值,而是在哈希映射中查找arg并返回值(如果存在)。如果不存在,缓存器将调用闭包并将结果值保存在与其arg值关联的哈希映射中 当前缓
Cacher
struct,以演示闭包和函数编程的使用。作为练习,他们鼓励读者尝试创建一个通用的缓存
,它可以存储多个值。为此,他们建议使用Hashmap
尝试修改缓存以保存哈希映射,而不是单个值。哈希映射的键将是传入的arg值,哈希映射的值将是对该键调用闭包的结果。value函数不会直接查看self.value是否有Some或None值,而是在哈希映射中查找arg并返回值(如果存在)。如果不存在,缓存器将调用闭包并将结果值保存在与其arg值关联的哈希映射中
当前缓存器实现的第二个问题是,它只接受接受接受一个类型为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))
}
}
结构缓存器
式中T:Fn(K)->V
{
计算:T,
值:HashMap,
}
嵌入式缓存器
其中T:Fn(K)->V,
K:std::cmp::Eq+std::hash::hash+Clone,
{
fn新(计算:T)->缓存{
缓存器{
计算,
值:HashMap::new(),
}
}
fn值(&mut self,强度:K)->&V{
self.values.entry(intensity.clone())或_insert((self.calculation)(intensity))
}
}
此代码编译并运行,但由于总是执行(self.calculation)(intensity)
,因此不能作为适当的缓存。即使条目存在。我从文档和示例中了解到,Entry::or_insert
函数仅在Entry
不存在时执行
我知道这个问题的答案,但我想知道是否有可能以我目前的方式解决这个问题
编辑:如注释中所述:或使用
插入并不能解决问题。当尝试使用(| |(self.calculation)(intensity.clone())
进行或插入时,我得到以下错误错误[E0502]:无法将self借用为不可变,因为它也借用为可变的
代码的问题是,在Rust中调用函数之前,始终会对函数参数进行求值(和大多数命令式语言)。这意味着在调用或\u insert()
之前,代码将无条件地调用(self.calculation)(intensity)
函数将在内部检查某个值是否已存在于条目中,如果没有,则只插入它作为参数传递的新值,但只有在调用了self.calculation
后才会发生这种情况
使用或\u insert\u 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))
}
}
使用std::collections::HashMap;
结构缓存器{
计算:T,
值:HashMap,
}
嵌入式缓存器
哪里
K:std::cmp::Eq+std::hash::hash+Clone,
{
fn新(计算:T)->缓存{
缓存器{
计算,
值:HashMap::new(),
}
}
fn值(&mut self,强度:K)->&V
哪里
T:Fn(K)->V,
{
let calculation=&self.calculation;
自我价值观
.entry(intensity.clone())
.或|插入|带(| |计算(强度))
}
}
value()
实现中的一个子节是,您需要在单独的变量中存储对self.calculation
的引用。否则,闭包将触发对self
的借用,这与调用self.values.entry()触发的对self.values
的可变借用重叠
。如果在外部范围内明确只借用self.calculation
,则借用检查器足够智能,可以确定它不会与self.values
重叠
作为旁注,我建议使用
rustfmt
来保持代码格式的一致性。我还建议将特征范围尽可能缩小,以避免不必要的重复。这两个建议都包含在上面的代码中。TL;DR:您尝试过或_insert_with
吗?我刚刚尝试过或| insert_with(| |(self.calculation)(intensity.clone())
并获得以下错误:错误[E0502]:不能借用'self'作为不可变的,因为它也是作为可变的
借用的。或_insert
和或_insert_with
之间有什么区别吗?除了前者需要一个值,后者需要一个返回值的闭包之外?嗯,这是真的,你不能这样做。你只需要存储一个refe请在前面一行中的一个单独的let
语句中执行calculation
。let calculation=&self.calculation;
。借用检查器非常聪明,可以看到self.calculation
和self.values
如果出现在同一范围内,但如果您只是使用self.calculation
在闭包内部,它将触发借用所有self
,这与self.values
@SvenMarnach重叠。非常感谢。解决方案是添加您提到的行,并使用@justinas建议使用或插入。