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

2024-04-05

我的目标是实施建议的改进缓存结构Rust 书第 13.1 章的内容是,创建一个结构体,该结构体接受一个函数并使用记忆来减少给定函数的调用次数。为此,我创建了一个结构体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()引用的是函数所拥有的价值(...)。但我不应该得到类型 V 的值的引用吗,它的所有者是HashMap?编译器是否会感到困惑,因为该值由函数拥有并保存为result一会儿?

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

请注意,需要暂时保存结果,因为insert方法消耗密钥等argRef不再有效。我也承认签名value可能会出现问题(请参阅从 HashMap 中可变借用和生命周期省略 https://stackoverflow.com/questions/56181266/mutable-borrow-from-hashmap-and-lifetime-elision)但我试图避免Copy特质绑定。

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

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

我们来看看OccupiedEntry::get() https://doc.rust-lang.org/stable/std/collections/hash_map/struct.OccupiedEntry.html#method.get的签名:

 pub fn get(&self) -> &V

这个签名告诉我们的是,从OccupiedEntry只能活到OccupiedEntry本身。但是,那OccupiedEntry是一个局部变量,因此当函数返回时它会被删除。

我们想要的是一个其生命周期与HashMap的一生。两个都Entry https://doc.rust-lang.org/stable/std/collections/hash_map/enum.Entry.html and OccupiedEntry https://doc.rust-lang.org/stable/std/collections/hash_map/struct.OccupiedEntry.html有一个生命周期参数('a),它链接到&mut self参数输入HashMap::entry https://doc.rust-lang.org/stable/std/collections/hash_map/struct.HashMap.html#method.entry。我们需要一个方法OccupiedEntry返回一个&'a V。没有这样的方法,但有一个返回一个'&a mut V: into_mut https://doc.rust-lang.org/stable/std/collections/hash_map/struct.OccupiedEntry.html#method.into_mut。可变引用可以隐式强制为共享引用,因此我们需要做的就是替换get() with 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)
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Entry::Occupied.get() 返回引用当前函数拥有的数据的值,即使 hashmap 应该拥有所有权 的相关文章

随机推荐