Hashmap 如何在不依赖复制特性的情况下在Rust中构建缓存?
我正试图实现一个Hashmap 如何在不依赖复制特性的情况下在Rust中构建缓存?,hashmap,rust,Hashmap,Rust,我正试图实现一个缓存器,正如铁锈书中提到的,但遇到了麻烦 我的缓存代码如下所示: use std::collections::HashMap; use std::hash::Hash; pub struct Cacher<T, K, V> where T: Fn(K) -> V, { calculation: T, values: HashMap<K, V>, } impl<T, K: Eq + Hash, V> Cacher
缓存器
,正如铁锈书中提到的,但遇到了麻烦
我的缓存
代码如下所示:
use std::collections::HashMap;
use std::hash::Hash;
pub struct Cacher<T, K, V>
where
T: Fn(K) -> V,
{
calculation: T,
values: HashMap<K, V>,
}
impl<T, K: Eq + Hash, V> Cacher<T, K, V>
where
T: Fn(K) -> V,
{
pub fn new(calculation: T) -> Cacher<T, K, V> {
Cacher {
calculation,
values: HashMap::new(),
}
}
pub fn value(&mut self, k: K) -> &V {
let result = self.values.get(&k);
match result {
Some(v) => {
return v;
}
None => {
let v = (self.calculation)(k);
self.values.insert(k, v);
&v
}
}
}
}
mod cacher;
#[cfg(test)]
mod tests {
use cacher::Cacher;
#[test]
fn repeated_runs_same() {
let mut cacher = Cacher::new(|x| x);
let run1 = cacher.value(5);
let run2 = cacher.value(7);
assert_ne!(run1, run2);
}
}
我在运行测试用例时遇到以下问题:
错误[E0499]:不能一次多次借用缓存作为可变缓存
每次我生成run1、run2值时,它都试图借用cacher
作为可变借用。我根本不明白它为什么要借阅-我想cacher.value()
应该返回对存储在cacher
中的项目的引用,而该项目不是借阅错误[E0597]:v的寿命不够长
在value()的None情况下指向v I返回值。如何正确地将v
移动到HashMap
中,并赋予它与HashMap
相同的生存期?很明显,生命周期在返回时即将过期,但我只想返回一个对它的引用,作为value()的返回error[E0502]:无法借用
self.values作为可变值,因为它在value()中也作为不可变的
借用self.values.get(&k)
是一个不可变的借阅,self.values.insert(k,v)
是一个可变的借阅-尽管我认为.get()
是一个不可变的借阅,.insert()
是一个所有权转让还有一些与移动有关的错误,我应该能够单独处理。这些都是更基本的错误,表明我误解了Rust中所有权的概念,但重读这本书的这一部分并不能让我清楚我错过了什么。我认为这里有很多问题需要研究: 首先,对于函数
值(&mut self,k:k)->&V
的定义;编译器将为您插入生存期,使其成为值(&'a mut self,k:k)->&'a V
。这意味着,self
的生存期不能为了函数而缩短,因为函数中有相同生存期的引用,并且将与作用域一样长。因为它是一个可变的引用,所以您不能再次借用它。因此错误错误[E0499]:不能一次多次借用缓存作为可变的
其次,调用calculation
函数,该函数返回函数value()
的某个内部范围内的值,然后返回对它的引用,这是不可能的。您希望引用比被引用者的寿命更长。因此错误[E0597]:v的寿命不够长
第三个错误有点复杂。你看,let result=self.values.get(&k)第一条语句中提到的code>导致k
保持不变,直到函数结束<返回的代码>结果将与函数值()
的有效期一样长,这意味着您不能在同一范围内借用(可变),从而导致错误
error[E0502]:无法将self.values作为可变项借用,因为它在value()self.values.get(&k)中也是作为不可变项借用的。
您的K
需要是克隆
,原因是K
将被移动到函数计算
,使其在插入
期间无法使用
因此,使用K
作为克隆
,缓存
实现将是:
impl<T, K: Eq + Hash + Clone, V> Cacher<T, K, V>
where
T: Fn(K) -> V,
{
pub fn new(calculation: T) -> Cacher<T, K, V> {
Cacher {
calculation,
values: hash_map::HashMap::new(),
}
}
pub fn value(&mut self, k: K) -> &V {
if self.values.contains_key(&k) {
return &self.values[&k];
}
self.values.insert(k.clone(), (self.calculation)(k.clone()));
self.values.get(&k).unwrap()
}
}
返回对某个值的引用与借用该值是一样的。由于该值属于缓存器,因此它也隐式借用缓存器。这是有道理的:如果您在缓存中引用一个值,然后销毁缓存,那么您的引用会发生什么情况?还要注意,如果修改缓存(例如,通过插入新元素),这可能会重新分配存储,这将使对存储在缓存中的值的任何引用无效
您需要将值至少设置为Clone
,以便Cacher::value
可以通过值而不是引用返回。如果您的值太昂贵而无法克隆,并且您同意所有调用者都使用相同的实例,则可以使用
获取存储在HashMap
中的实例的简单方法是在映射中插入值后调用self.values.get(k).unwrap()
,而不是分配用于构建实例的临时方法。为了避免计算两倍于地图中值位置的成本,您可以使用以下界面:
pub fn value(&mut self, k: K) -> Rc<V> {
self.values.entry (&k).or_insert_with (|| Rc::new (self.calculation (k)))
}
pub-fn值(&mut-self,k:k)->Rc{
self.values.entry(&k).或插入带有(| | Rc::new(self.calculation(k)))
}
我相信我对第2点的回答也解决了这一点
您的代码根本无法编译。请把它修好!对于Rc(还不允许评论@jmb的回答):谢谢Vikram。我可以得到更多关于测试用例本身的细节吗?我试图在不使用Rc的情况下实现它(为了熟悉该语言),但再次遇到了双可变借用问题。考虑到它只引用了V,这不应该是个问题@同样,是的,value()
的函数签名本身是好的,但要与测试一起工作,则不是。(如前所述)。我明白了,您已经声明V
为Clone
类型,那么为什么不将签名更改为fn value(&mut self,k:k)->V{..
?因为您不想使用Rc
。@WarSame,另一种方法可以是使用原始指针。这将允许您保留value()的签名
完整和V
不要求是克隆类型。请注意,使用原始指针通常被认为是“不安全的”,但如果严格地说这个特殊情况,它似乎很好。感谢Vikram。我是一个全新的生锈者,还没有尝试过克隆或许多不同的元素。
pub fn value(&mut self, k: K) -> Rc<V> {
self.values.entry (&k).or_insert_with (|| Rc::new (self.calculation (k)))
}