簡體   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