如何在不依赖复制特征的情况下在Rust中构建Cacher? [英] How do I build a Cacher in Rust without relying on the Copy trait?

查看:73
本文介绍了如何在不依赖复制特征的情况下在Rust中构建Cacher?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现

I am trying to implement a Cacher as mentioned in Chapter 13 of the Rust book and running into trouble.

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

我对该库的测试用例如下:

and my test case for this lib looks like:

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

运行测试用例时遇到以下问题:

I ran into the following problems when running my test case:

  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指向v我在value()为None的情况下返回.如何正确地将v移到HashMap中并赋予其与HashMap相同的寿命?显然,生存期随着它的返回而到期,但是我只想返回对其的引用以用作value()的返回.
  3. value()中的
  4. error[E0502]: cannot borrow self.values as mutable because it is also borrowed as immutable. self.values.get(&k)是不可变的借项,而self.values.insert(k,v)是可变的借项-尽管我认为.get()是不可变的借项,而.insert()是所有权转移.
  1. error[E0499]: cannot borrow cacher as mutable more than once at a time Each time I make a run1, run2 value it is trying to borrow cacher as a mutable borrow. I don't understand why it is borrowing at all - I thought cacher.value() should be returning a reference to the item that is stored in the cacher which is not a borrow.
  2. error[E0597]: v does not live long enough pointing to the v I return in the None case of value(). How do I properly move the v into the HashMap and give it the same lifetime as the HashMap? Clearly the lifetime is expiring as it returns, but I want to just return a reference to it to use as the return from value().
  3. error[E0502]: cannot borrowself.valuesas mutable because it is also borrowed as immutable in value(). self.values.get(&k) is an immutable borrow and self.values.insert(k,v) is a mutable borrow - though I thought .get() was an immutable borrow and .insert() was a transfer of ownership.

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

and a few other errors related to moving which I should be able to handle separately. These are much more fundamental errors that indicate I have misunderstood the idea of ownership in Rust, yet rereading the segment of the book doesn't make clear to me what I missed.

推荐答案

我认为这里有很多问题需要探讨:

I think there a quite a few issues to look into here:

首先,对于函数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.

First, for the definition of the function value(&mut self, k: K) -> &V ; the compiler will insert the lifetimes for you so that it becomes value(&'a mut self, k: K) -> &'a V. This means, the lifetime of the self cannot shrink for the sake of the function, because there is reference coming out of the function with the same lifetime, and will live for as long as the scope. Since it is a mutable reference, you cannot borrow it again. Hence the error error[E0499]: cannot borrow cacher as mutable more than once at a time.

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

Second, you call the calculation function that returns the value within some inner scope of the function value() and then you return the reference to it, which is not possible. You expect the reference to live longer than the the referent. Hence the error 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)

The third error is a bit involved. You see, let result = self.values.get(&k); as mentioned in the first statement, causes k to be held immutably till the end of the function. result returned will live for as long your function value(), which means you cannot take a borrow(mutable) in the same scope, giving the error 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中不可用.

Your K needs to be a Clone, reason being k will be moved into the function calculation, rendering it unusable during insert.

因此,以K作为CloneCacher的实现将是:

So with K as a Clone, the Cacher implementation will be:

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(...获取(并返回)的引用将被该微小作用域所取代.

This lifetimes here are based on the branching control flow. The if self.values.contains_key ... block always returns, hence the code after if block can only be executed when if self.values.contains_key ... is false. The tiny scope created for if condition, will only live within the condition check, i.e reference taken (and returned) for if self.values.contains_key(... will go away with this tiny scope.

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

For more please refer NLL RFC

正如@jmb在他的回答中提到的那样,为了使测试正常进行,V将需要为Clone(impl <... V:Clone> Cacher<T, K, V>)以按值返回或使用诸如Rc的共享所有权来避免克隆费用.

As mentioned by @jmb in his answer, for your test to work, V will need to be a Clone (impl <... V:Clone> Cacher<T, K, V>) to return by value or use shared ownership like Rc to avoid the cloning cost.

例如

fn value(&mut self, k: K) -> V  { ..
fn value(&mut self, k: K) -> Rc<V> { ..

这篇关于如何在不依赖复制特征的情况下在Rust中构建Cacher?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆