Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Hashmap 如何在不依赖复制特性的情况下在Rust中构建缓存?_Hashmap_Rust - Fatal编程技术网

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)导致
    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)))
    }