Entry::Occupied.get() returns 引用当前函数拥有的数据的值,即使 hashmap 应该拥有所有权

Entry::Occupied.get() returns a value referencing data owned by the current function even though hashmap should have the ownership

我的目标是实现对 rust book 第 13.1 章的 cacher struct 的建议改进,即创建一个采用函数并使用 memoization 来减少数量的结构给定函数的调用。为此,我创建了一个带有 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() "returns a value referencing data owned by the current function".

我的理解是,编译器认为 occEntry.get() 所引用的值属于函数 value(.. .)。但是我不应该得到 HashMap 所拥有的类型 V 的值的引用吗?编译器是否因为该值归函数所有并在短时间内保存为 result 而感到困惑?

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

请注意,需要暂时保存结果,因为insert方法消耗了key,argRef不再有效.此外,我承认 value 的签名可能存在问题(参见 Mutable borrow from HashMap and lifetime elision),但我试图避免 Copy Trait Bound。

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

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

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

 pub fn get(&self) -> &V

这个签名告诉我们的是,从 OccupiedEntry 获得的引用只能与 OccupiedEntry 本身一样长。然而,OccupiedEntry 是一个局部变量,因此当函数 returns.

时它被删除

我们想要的是一个生命周期绑定到HashMap生命周期的引用。两者 Entry and OccupiedEntry have a lifetime parameter ('a), which is linked to the &mut self parameter in HashMap::entry. We need a method on OccupiedEntry that returns a &'a V. There's no such method, but there's one that returns a '&a mut V: into_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)
        }
    }
}