これを解決するために(別の回答で提案されているように)使用thread-local!
するのはそれほど簡単ではなかったので、ここに完全な解決策を含めます:
use std::cell::RefCell;
use std::collections::HashMap;
fn main() {
println!("thread-local demo for Collatz:");
(2..20).for_each(|n| println!("{n}: {c}", n = n, c = collatz(n)));
}
thread_local! (static COLLATZ_CACHE: RefCell<HashMap<usize, usize>> = {
let mut cache = HashMap::new();
cache.insert(1, 0);
RefCell::new(cache)
});
fn collatz(n: usize) -> usize {
COLLATZ_CACHE.with(|cache| {
let entry = cache.borrow().get(&n).copied();
if let Some(v) = entry { v } else {
let v = match n % 2 {
0 => 1 + collatz(n / 2),
1 => 1 + collatz(n * 3 + 1),
_ => unreachable!(),
};
cache.borrow_mut().insert(n, v);
*cache.borrow().get(&n).unwrap()
}
})
}
スレッド ローカル ストレージが十分にグローバルでない場合は、once_cell クレートの機能を使用できます。これは std (すでに nightly) にも導入されていますが、静的変数を初期化します。
#![feature(once_cell)]
use std::collections::HashMap;
use std::lazy::SyncLazy;
use std::sync::Mutex;
fn main() {
println!("once_cell demo for Collatz:");
(2..20).for_each(|n| println!("{n}: {c}", n = n, c = collatz(n)));
}
static COLLATZ_CACHE: SyncLazy<Mutex<HashMap<usize, usize>>> = SyncLazy::new(|| {
let mut cache = HashMap::new();
cache.insert(1, 0);
Mutex::new(cache)
});
fn collatz(n: usize) -> usize {
let cache = &COLLATZ_CACHE;
let entry = cache.lock().unwrap().get(&n).copied();
if let Some(v) = entry {
v
} else {
let v = match n % 2 {
0 => 1 + collatz(n / 2),
1 => 1 + collatz(n * 3 + 1),
_ => unreachable!(),
};
cache.lock().unwrap().insert(n, v);
*cache.lock().unwrap().get(&n).unwrap()
}
}
遊び場