如何在 Rust 中为延迟计算的值正确实现缓存结构?

Dav*_*ski 6 rust

在 Rust 中,我正在寻找一种更简洁和惯用的方法来为昂贵的计算定义缓存。

The Rust Programming Language第二版的第 13 章中,作者为读者留下了一个练习来重构一个结构以返回延迟计算的值。

经过四天的反复试验,我想出了:

use std::collections::HashMap;

#[allow(dead_code)]
struct Cache<T> 
    where T: Fn(u32) -> u32
{
    calculation: T,
    internal: HashMap<u32, u32>
}

#[allow(dead_code)]
impl<T> Cache<T>
    where T: Fn(u32) -> u32
{
    fn new(calculation: T) -> Cache<T> {
        Cache {
            calculation,
            internal: HashMap::new(),
        }
    }

    fn set(&mut self, arg: u32, value: u32) -> u32 {
        self.internal.insert(arg, value);
        self.get(arg)
    }

    fn get(&mut self, arg: u32) -> u32 {
        self.internal[&arg]
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.internal.contains_key(&arg) {
            true => {
                self.get(arg)
            },
            false => {
                self.set(arg, (self.calculation)(arg))
            },
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对于测试功能:

#[test]
fn call_with_different_values() {
    let mut c = Cache::new(|a| a);
    let _v1 = c.value(1);
    let v2 = c.value(2);
    assert_eq!(v2, 2);
}
Run Code Online (Sandbox Code Playgroud)

我可以改进impl吗?有没有更可接受的方式来写这个?

Ry-*_*Ry- 5

  • set()self.get(arg)可以简化为value

  • get()只需要一个非可变的&self.

  • get()并且一开始set()看起来并没有那么有用。

  • hash_map::EntryAPI可以帮助您简化value()

    fn value(&mut self, arg: u32) -> u32 {
        let Self { internal, calculation } = self;
        let entry = internal.entry(arg);
        *entry.or_insert_with(|| (calculation)(arg))
    }
    
    Run Code Online (Sandbox Code Playgroud)

然后是次要风格的事情:

  • 您可以使用Self而不是在impl.

  • 尾随逗号很好。

在所有:

use std::collections::HashMap;

pub struct Cache<T>
    where T: Fn(u32) -> u32
{
    calculation: T,
    internal: HashMap<u32, u32>,
}

impl<T> Cache<T>
    where T: Fn(u32) -> u32
{
    pub fn new(calculation: T) -> Cache<T> {
        Self {
            calculation,
            internal: HashMap::new(),
        }
    }

    pub fn value(&mut self, arg: u32) -> u32 {
        let Self { internal, calculation } = self;
        let entry = internal.entry(arg);
        *entry.or_insert_with(|| (calculation)(arg))
    }
}

#[test]
fn call_with_different_values() {
    let mut c = Cache::new(|a| a);
    let _v1 = c.value(1);
    let v2 = c.value(2);
    assert_eq!(v2, 2);
}
Run Code Online (Sandbox Code Playgroud)

操场