繁体   English   中英

如何在不依赖复制特征的情况下在Rust中构建Cacher?

[英]How do I build a Cacher in Rust without relying on the Copy trait?

我正在尝试实现Rust书第13章中提到的Cacher并遇到麻烦。

我的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);
    }
}

在运行测试用例时,我遇到了以下问题:

  1. error[E0499]: cannot borrow cacher as mutable more than once at a time每次我生成run1,run2值时,它都试图借用cacher作为可变借项。 我不明白为什么它是借用在所有-我想cacher.value()应该返回到存储在该项目的引用cacher是不是借。
  2. error[E0597]: v does not live long enough指向在value()为None的情况下返回的v。 如何正确地将v移到HashMap并赋予其与HashMap相同的生存期? 显然,生命周期将随着它的返回而到期,但是我只想返回对其的引用以用作value()的返回。
  3. error[E0502]: cannot borrow as mutable because it is also borrowed as immutable self.values as mutable because it is also borrowed as immutable在value()中as mutable because it is also borrowed as immutable self.values。 self.values.get(&k)是不可变的借用, self.values.insert(k,v)是可变的借用-尽管我认为.get()是不可变的借用,而.insert()是所有权转移。

以及其他一些与移动有关的错误,我应该能够分别处理。 这些是更根本的错误,表明我对Rust的所有权概念有误解,但重新阅读本书的内容并不清楚我错过了什么。

我认为这里有很多问题需要研究:

首先,对于函数value(&mut self, k: K) -> &V的定义value(&mut self, k: K) -> &V ; 编译器将为您插入生存期,使其变为value(&'a mut self, k: K) -> &'a V 这意味着, self的生存期不会因功能而缩短,因为存在具有相同生存期的函数引用,并且生存期与作用域一样长。 由于它是可变参考,因此您不能再次借用它。 因此,错误error[E0499]: cannot borrow cacher as mutable more than once at a time

其次,调用calculation函数,该函数在函数value()某些内部范围内返回值,然后返回对其的引用,这是不可能的。 您期望参考的寿命比参考对象的寿命长。 因此,错误error[E0597]: v does not live long enough

第三个错误涉及。 您会看到, let result = self.values.get(&k); 如第一条语句所述,使k不变地保持到函数结束。 返回的result将在您的函数value()保留很长时间,这意味着您不能在相同的范围内借用(可变),从而导致错误error[E0502]: cannot borrow self.values as mutable because it is also borrowed as immutable in value() self.values.get(&k)

您的K必须是Clone ,原因是k将被移入函数calculation ,从而使其在insert期间无法使用。

因此,使用K作为CloneCacher实现将是:

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()
    }
}

这里的寿命基于分支控制流。 if self.values.contains_key ...块始终返回,因此,仅当if self.values.contains_key ...false时,才能执行if块之后的代码。 if条件创建的微小作用域仅存在于条件检查中,即, if self.values.contains_key(...将在这个微小作用域中消失,则采用此引用(并返回)。

有关更多信息,请参阅NLL RFC

正如@jmb在他的答案中提到的那样,为了使测试正常工作, V将需要成为Cloneimpl <... V:Clone> Cacher<T, K, V> )以按值返回或使用诸如避免了Rc的克隆成本。

例如。

fn value(&mut self, k: K) -> V  { ..
fn value(&mut self, k: K) -> Rc<V> { ..
  1. 返回对值的引用与借用该值相同。 由于该值归缓存器所有,因此它也隐式借用了缓存器。 这是有道理的:如果您引用了缓存器中的某个值,然后销毁了该缓存器,您的引用又会怎样? 还要注意,如果您修改缓存器(例如,通过插入新元素),则可能会重新分配存储,这将使对存储在其中的值的任何引用无效。

    您需要您的值至少是Clone以便Cacher::value可以按值而不是按引用返回。 如果您的值太昂贵而无法克隆,并且所有调用方都获得相同的实例是可以的,则可以使用Rc

  2. 与存储在HashMap的实例(而不是分配给您的临时实例)相对,天真的获取该实例的方法是在将值插入映射后调用self.values.get (k).unwrap() 为了避免计算值在地图中的位置两倍的开销,可以使用Entry接口:

     pub fn value(&mut self, k: K) -> Rc<V> { self.values.entry (&k).or_insert_with (|| Rc::new (self.calculation (k))) } 
  3. 我相信我对第2点的回答也可以解决这一点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM