Entry::Occupied.get()返回引用当前函数拥有的数据,尽管哈希表应该拥有所有权。

10

我的目标是在Rust书第13.1章中实现对cacher结构的建议改进,即创建一个结构体,它接受一个函数并使用记忆化技术来减少调用给定函数的次数。为了实现这一点,我创建了一个具有HashMap的结构体。

struct Cacher<T, U, V>
where T: Fn(&U) -> V, U: Eq + Hash
{
  calculation: T,
  map: HashMap<U,V>,
}

有两种方法,一种是构造函数,另一种是负责备忘的函数。

impl<T, U, V> Cacher<T, U, V>
    where T: Fn(&U) -> V, U: Eq + Hash
{
    fn new(calculation: T) -> Cacher<T,U,V> {
        Cacher {
            calculation,
            map: HashMap::new(),
        }
    }

    fn value(&mut self, arg: U) -> &V {
        match self.map.entry(arg){
            Entry::Occupied(occEntry) => occEntry.get(),
            Entry::Vacant(vacEntry) => {
                let argRef = vacEntry.key();
                let result = (self.calculation)(argRef);
                vacEntry.insert(result)
            }
        }
    }
}

我使用了Entry枚举,因为我没有找到更好的方法来确定HashMap是否包含一个键,如果它没有,则计算该值并将其插入到HashMap中,同时返回对它的引用。

如果我想编译上面的代码,我会得到一个错误,指出occEntry被它的.get()方法借用了(这对我来说很好),而.get()“返回引用当前函数拥有数据的值”。

我的理解是,编译器认为occEntry.get()所引用的值是由value(...)函数拥有的。但是,我不应该获取类型为V的值的引用,该引用由HashMap拥有吗?编译器是否因为该值被函数拥有并作为result短暂保存而感到困惑?

let result = (self.calculation)(argRef);
vacEntry.insert(result)

请注意,由于插入方法消耗了密钥,因此需要临时保存结果,这样argRef就不再有效。此外,我承认value的签名可能存在问题(请参见可变从HashMap中借用和生命周期省略),但我尝试避免Copy特征绑定。

为了快速重现问题,我附加了必要的use语句。感谢您的帮助。

use std::collections::HashMap;
use std::cmp::Eq;
use std::hash::Hash;
use std::collections::hash_map::{OccupiedEntry, VacantEntry, Entry};
1个回答

14

让我们来看一下 OccupiedEntry::get() 的签名:

 pub fn get(&self) -> &V
这个签名告诉我们的是,从OccupiedEntry获取的引用只能在OccupiedEntry本身存在时才有效。但是,OccupiedEntry是一个局部变量,在函数返回时就会被删除。
我们需要的是一个引用的生命周期与HashMap的生命周期绑定。 EntryOccupiedEntry都有一个生命周期参数('a),它与HashMap::entry中的&mut self参数相关联。我们需要在OccupiedEntry上创建一个方法,该方法返回一个&'a V。虽然没有这样的方法,但有一个方法可以返回'&a mut Vinto_mut。可变引用可以隐式地强制转换为共享引用,因此我们只需要将get()替换为into_mut()即可使你的方法编译。
fn value(&mut self, arg: U) -> &V {
    match self.map.entry(arg) {
        Entry::Occupied(occ_entry) => occ_entry.into_mut(),
        Entry::Vacant(vac_entry) => {
            let arg_ref = vac_entry.key();
            let result = (self.calculation)(arg_ref);
            vac_entry.insert(result)
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接