【发布时间】:2016-03-26 02:16:00
【问题描述】:
我有这样一个昂贵的功能:
pub fn get_expensive_value(n: u64): u64 {
let ret = 0;
for 0 .. n {
// expensive stuff
}
ret
}
而且它被非常频繁地使用相同的参数调用。它是纯粹的,这意味着它将返回相同的结果并且可以使用缓存。
如果这是一个结构体方法,我会在结构体中添加一个成员作为缓存,但事实并非如此。所以我的选择似乎是使用静态:
static mut LAST_VAL: Option<(u64, u64)> = None;
pub fn cached_expensive(n: u64) -> u64 {
unsafe {
LAST_VAL = LAST_VAL.and_then(|(k, v)| {
if k == n {
Some((n,v))
} else {
None
}
}).or_else(|| {
Some((n, get_expensive_value(n)))
});
let (_, v) = LAST_VAL.unwrap();
v
}
}
现在,我不得不使用unsafe。代替static mut,我可以将RefCell 放入const。但我不相信这更安全 - 它只是避免使用 unsafe 块。我想过Mutex,但我认为这也不会让我获得线程安全。
重新设计代码以使用结构进行存储并不是一个真正的选择。
【问题讨论】:
-
或者您可以更改
cached_expensive的签名以接受缓存作为另一个参数。 -
我不认为这是重复的。我的问题是专门关于缓存的,虽然我的出发点是一个全局的、可变的单例,但这是偶然的,一个好的解决方案可能会解释(例如)
RefCell或Mutex如何使它变得更好,或者提供一个完全结构上不同的选择。 -
我不同意,但在其他人同意我之前,我不会使用魔法dupehammer。简而言之,没有其他地方可以存储数据。您要么传入一个存储位置(通过显式参数或通过
self隐式传递),要么必须将其存储在全局存储中。后者的限制是您需要处理对缓存的并发访问。 -
虽然我现在还不能完全推理出来,但我觉得问题中提议的
unsafe块不支持所需的安全保证。例如,我很确定对函数的并发调用可以对存储进行部分读取/写入(因为没有互斥),从而在任意情况下导致奇怪的行为。
标签: caching rust static-members