Rust Book 中的闭包缓存示例的生命周期注释

发布于 2025-01-14 21:23:50 字数 1636 浏览 0 评论 0原文

尝试进入 Rust 状态,到目前为止过得很愉快。

一直在阅读官方 Rust 书籍:The Rust 编程语言 并拥有关于闭包章节出现了一个问题。

本章创建了一个带有缓存机制的闭包函数。任务是重写实现以使用 HashMap 而不是 Option 来缓存多个结果,并使用泛型来使用除 u32 以外的更多类型。

我当前的工作代码如下所示:


struct Cacher<'a, T, K: 'a>
where
    T: Fn(&'a K) -> &'a K,
{
    calculation: T,
    cache: HashMap<&'a K, &'a K>,
}

impl<'a, T, K> Cacher<'a, T, K>
where
    T: Fn(&'a K) -> &'a K,
    K: Hash + Eq,
{
    fn new(calculation: T) -> Cacher<'a, T, K> {
        Cacher {
            calculation,
            cache: HashMap::new(),
        }
    }
    fn value(&mut self, arg: &'a K) -> &'a K {
        match self.cache.get(arg) {
            Some(v) => v,
            None => {
                let v= (self.calculation)(arg);
                self.cache.insert(arg, v);
                v
            }
        }
    }
}

// inside the generate_workout function, the closure is to mock a long task
fn generate_workout(intensity: u32) {
    let mut expensive_result = Cacher::new(|num: &u32| {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        &num
    });

    println!("Result: {}", expensive_result.value(&intensity));
}

我的问题是关于缓存器结构和实现所需的生命周期注释。是否需要它,因为我在generate_workout函数中用于特定缓存器expense_result的闭包正在使用参数num作为它的返回值?

非常感谢。

Trying to get into rust and had a pretty good time so far.

Been working through the official Rust Book: The Rust Programming Language and had a question come up about the closures chapter.

The chapter creates a closure function with a cache mechanism. The task is to rewrite the implementation to use a HashMap instead of an Option to cache multiple results and use Generics to use more types than just u32.

My current working code looks like the following:


struct Cacher<'a, T, K: 'a>
where
    T: Fn(&'a K) -> &'a K,
{
    calculation: T,
    cache: HashMap<&'a K, &'a K>,
}

impl<'a, T, K> Cacher<'a, T, K>
where
    T: Fn(&'a K) -> &'a K,
    K: Hash + Eq,
{
    fn new(calculation: T) -> Cacher<'a, T, K> {
        Cacher {
            calculation,
            cache: HashMap::new(),
        }
    }
    fn value(&mut self, arg: &'a K) -> &'a K {
        match self.cache.get(arg) {
            Some(v) => v,
            None => {
                let v= (self.calculation)(arg);
                self.cache.insert(arg, v);
                v
            }
        }
    }
}

// inside the generate_workout function, the closure is to mock a long task
fn generate_workout(intensity: u32) {
    let mut expensive_result = Cacher::new(|num: &u32| {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        &num
    });

    println!("Result: {}", expensive_result.value(&intensity));
}

My question is about the needed lifetime annotation for the cacher struct and implementation. Is it needed, because my closure used for the specific cacher expensive_result in the generate_workout function is using the argument num as it's return value?

Thank you very much.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ぶ宁プ宁ぶ 2025-01-21 21:23:50

我的问题是关于缓存器结构和实现所需的生命周期注释。是否需要

您的问题的直接答案是需要;每当您在 struct 中使用引用时,您必须使用生命周期对其进行注释(请参阅此处)。

但是,我怀疑您已经知道这一点,但这并不是您真正要问的。因此,一些猜测、推测和一般建议如下!

当前的实现比需要的限制更多,这迫使您添加大量生命周期注释“噪音”。实际上,您可以稍微简化 Cacher struct (通过删除 where 子句并删除多余的 K: 'a > 绑定)但这在这里并不重要。

首先要问的问题是,我想要 Cacher 拥有其数据还是借用它?正如目前所写的,Cacher 的定义仅允许后者。

可能对您有帮助的一个关键见解是了解 T 仅包含拥有的类型 是一种误解。

一旦您了解,您就可以重写Cacher,以便它可以将键和值保存为拥有的类型或引用(假设这是您想要的灵活性)。

您问的另一个问题是您是否希望对缓存的键和值强制执行相同的要求。正如所写的,键和值必须具有相同的(引用)类型,这是相当严格的。这也意味着也必须是Eq + Hash,这可能不是您想要的。通常,您希望为键和值指定单独的通用参数(即 K、V)。

最后,您可能还想看看使用 HashMap::entry 用于您的 value 实现的 API。

My question is about the needed lifetime annotation for the cacher struct and implementation. Is it needed

The direct answer to your question is yes; whenever you use a reference inside a struct you must annotate it with a lifetime (see here).

However, I suspect you already know that and isn't what you are really asking. Therefore some guesswork, speculation and generic advise follows!

The current implementation is more restrictive than need be, and this is forcing you add a lot of lifetime annotation "noise". You can actually simplify the Cacher struct a bit (by removing the where clause and removing the redundant K: 'a bound) but that isn't important here.

The first question to ask is, do I want Cacher to own its data or borrow it? As currently written the definition of Cacher only allows for the latter.

A key insight that may help you is understanding that T only contains owned types is a misconception.

Once you understand that you can rewrite Cacher such that it may hold keys and values as either owned types or references (assuming that is flexibility that you want).

The other question you ask is whether you want to enforce the same requirements on both the keys and values of the cache. As written both keys and values must be of the same (reference) type which is quite restrictive. It also means that the values must also be Eq + Hash which likely isn't what you want. Typically you want to specify separate generic parameters (i.e. K, V) for the key and value.

Finally, you may also want to look at using the HashMap::entry API for your value implementation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文