5个不稳定版本

0.4.3 2024年1月2日
0.4.0 2023年1月18日
0.3.2 2022年12月2日
0.2.1 2022年1月1日
0.2.0 2021年12月24日

#347缓存

Download history 4331/week @ 2024-04-08 4868/week @ 2024-04-15 4054/week @ 2024-04-22 4064/week @ 2024-04-29 4197/week @ 2024-05-06 3979/week @ 2024-05-13 4430/week @ 2024-05-20 4338/week @ 2024-05-27 3944/week @ 2024-06-03 3124/week @ 2024-06-10 2781/week @ 2024-06-17 3144/week @ 2024-06-24 2549/week @ 2024-07-01 3180/week @ 2024-07-08 2894/week @ 2024-07-15 3186/week @ 2024-07-22

11,978 每月下载量
15 个Crates中使用(通过 memoize

MIT 许可证

21KB
365 代码行

memoize

Docs.rs Crates.rs CI

为相对简单的Rust函数提供一个#[memoize]属性:也就是说,具有一个或多个可Clone的参数,以及一个可Clone的返回类型的函数。就这么简单。

新闻:该库已更新,因此您无需单独导入lrulazy_static和其他依赖项。现在应该可以自动工作。请记住启用full功能以使用LRU缓存和其他附加功能。

阅读文档(cargo doc --open)以了解详细信息,或查看examples/,如果您想了解更多信息

// From examples/test2.rs

use memoize::memoize;

#[memoize]
fn hello(arg: String, arg2: usize) -> bool {
  arg.len()%2 == arg2
}

fn main() {
  // `hello` is only called once here.
  assert!(! hello("World".to_string(), 0));
  assert!(! hello("World".to_string(), 0));
  // Sometimes one might need the original function.
  assert!(! memoized_original_hello("World".to_string(), 0));
}

这可以扩展为(经过一些简化)

std::thread_local! {
  static MEMOIZED_MAPPING_HELLO : RefCell<HashMap<(String, usize), bool>> = RefCell::new(HashMap::new());
}

pub fn memoized_original_hello(arg: String, arg2: usize) -> bool {
  arg.len() % 2 == arg2
}

#[allow(unused_variables)]
fn hello(arg: String, arg2: usize) -> bool {
  let ATTR_MEMOIZE_RETURN__ = MEMOIZED_MAPPING_HELLO.with(|ATTR_MEMOIZE_HM__| {
    let mut ATTR_MEMOIZE_HM__ = ATTR_MEMOIZE_HM__.borrow_mut();
    ATTR_MEMOIZE_HM__.get(&(arg.clone(), arg2.clone())).cloned()
  });
  if let Some(ATTR_MEMOIZE_RETURN__) = ATTR_MEMOIZE_RETURN__ {
    return ATTR_MEMOIZE_RETURN__;
  }

  let ATTR_MEMOIZE_RETURN__ = memoized_original_hello(arg.clone(), arg2.clone());

  MEMOIZED_MAPPING_HELLO.with(|ATTR_MEMOIZE_HM__| {
    let mut ATTR_MEMOIZE_HM__ = ATTR_MEMOIZE_HM__.borrow_mut();
    ATTR_MEMOIZE_HM__.insert((arg, arg2), ATTR_MEMOIZE_RETURN__.clone());
  });

  r
}

附加功能

如上例所示,每个线程默认都有自己的缓存。如果您希望每个线程共享相同的缓存,您可以像下面这样指定SharedCache选项,以便将缓存包装在std::sync::Mutex中。例如

#[memoize(SharedCache)]
fn hello(key: String) -> ComplexStruct {
  // ...
}

您可以选择使用LRU缓存。事实上,如果您知道被缓存的函数有无限多的不同输入,您应该这样做!在这种情况下,使用如下属性

// From examples/test1.rs
// Compile with --features=full
use memoize::memoize;

#[derive(Debug, Clone)]
struct ComplexStruct {
  // ...
}

#[memoize(Capacity: 123)]
fn hello(key: String) -> ComplexStruct {
  // ...
}

增加更多缓存和配置选项相对简单,只需解析属性参数。目前,如果您未启用具有 full 功能,使用例如 Capacity 这样的参数进行编译将会失败。

另一个参数是 TimeToLive,指定缓存值允许存活的时间。

#[memoize(Capacity: 123, TimeToLive: Duration::from_secs(2))]

还可以使用 chrono::Duration,但需要首先将其转换为 std::time::Duration

#[memoize(TimeToLive: chrono::Duration::hours(3).to_std().unwrap())]

缓存值永远不会超过提供的持续时间,而是在下一个请求时重新计算。

您还可以指定一个自定义哈希器,例如使用 CustomHasherAHash

#[memoize(CustomHasher: ahash::HashMap)]

由于某些哈希器的初始化函数不是 new(),您可以通过指定一个 HasherInit 函数调用。

#[memoize(CustomHasher: FxHashMap, HasherInit: FxHashMap::default())]

有时,您不能或不想将数据作为缓存的一部分存储。在这些情况下,您可以使用 #[memoize] 宏中的 Ignore 参数来忽略一个参数。任何被 Ignore 的参数不再需要是可 Clone 的,因为它们不是作为参数集的一部分存储的,并且更改一个被 Ignore 的参数不会触发再次调用函数。您可以通过多次指定 Ignore 参数来忽略多个参数。

// `Ignore: count_calls` lets our function take a `&mut u32` argument, which is normally not
// possible because it is not `Clone`-able.
#[memoize(Ignore: count_calls)]
fn add(a: u32, b: u32, count_calls: &mut u32) -> u32 {
    // Keep track of the number of times the underlying function is called.
	*count_calls += 1;
	a + b
}

刷新

如果您对函数 f 进行了记忆化,将会有一个名为 memoized_flush_f() 的函数,允许您清除记忆化缓存。

贡献

...总是受欢迎!这将是我的第一个过程宏 crate,我非常感激对功能和风格的改进。请发送一个 pull request,如果需要一段时间才能审查,请不要气馁;我有时在这里会慢一些 :) -- Lewin

依赖

~1.5MB
~36K SLoC