Каков идиоматический способ реализации кэширования для функции, которая не является методом структуры?
У меня есть такая дорогая функция:
pub fn get_expensive_value(n: u64): u64 {
let ret = 0;
for 0 .. n {
// expensive stuff
}
ret
}
И он вызывается очень часто с одним и тем же аргументом. Он чистый, так что это означает, что он вернет тот же результат и может использовать кэш.
Если бы это был метод struct, я бы добавил член в структуру, которая действует как кэш, но это не так. поэтому мой вариант, кажется, использовать статический:
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
, но я не думаю, что это поможет мне обеспечить безопасность потока.
Перепроектирование кода для использования структуры в качестве хранилища на самом деле не является вариантом.
1 ответ:
Я думаю, что лучшей альтернативой является использование глобальной переменной с мьютексом. Использование lazy_static упрощает задачу и позволяет использовать "глобальное" объявление внутри функции
pub fn cached_expensive(n: u64) -> u64 { use std::sync::Mutex; lazy_static! { static ref LAST_VAL: Mutex<Option<(u64, u64)>> = Mutex::new(None); } let mut last = LAST_VAL.lock().unwrap(); let r = last.and_then(|(k, v)| { if k == n { Some((n, v)) } else { None } }).or_else(|| Some((n, get_expensive_value(n)))); let (_, v) = r.unwrap(); *last = r; v }