問題概述
值快取是一種更加廣泛的實用行為,我們可能希望在程式碼中的其他閉包中也使用他們。然而,目前 Cacher 的實現存在兩個小問題,這使得在不同上下文中複用變得很困難。
第一個問題是 Cacher 例項假設對於 value 方法的任何 arg 引數值總是會返回相同的值。也就是說,這個 Cacher 的測試會失敗:
《rust程式設計語言》13章閉包內容提出的問題
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 2);
}
具體程式碼請在《rust程式設計語言》第13章第一節 Cacher
實現的限制 中找到。
解決思路
嘗試修改 Cacher 存放一個雜湊 map 而不是單獨一個值。雜湊 map 的 key 將是傳遞進來的 arg 值,而 value 則是對應 key 呼叫閉包的結果值。
相比之前檢查 self.value 直接是 Some 還是 None 值,現在 value 函式會在雜湊 map 中尋找 arg,如果找到的話就返回其對應的值。如果不存在,Cacher 會呼叫閉包並將結果值儲存在雜湊 map 對應 arg 值的位置。
《rust程式設計語言》13章閉包內容提出的解決思路
解決方案
更改Cacher結構體的型別
use std::collections::HashMap;
struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: HashMap<u32, u32>,
}
將之前儲存單一u32型別的 value欄位 替換成 HashMap型別,此HashMap的key和value都為u32型別。
更改chacher結構體的方法
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: HashMap::new(),
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value.get(&arg) {
Some(v) => *v,
None => {
let v = (self.calculation)(arg);
self.value.insert(arg, v);
arg
}
}
}
}
new方法中返回的Cacher例項,value欄位 不再為Option<u32>型別,取而代之的是一個 被初始化的HashMap,用於存放 不同引數的結果快取。
value方法中,不再直接匹配結構體的value欄位,而是通過 引數 去value欄位的 HashMap 中找到儲存的值並返回,要是找不到則在HashMap中插入 key值為傳入引數,value值為結構體閉包呼叫引數所得的結果。
測試結果
fn main() {
let mut cal = Cacher::new(|num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
});
println!("{}", cal.value(1));
println!("{}", cal.value(2));
println!("{}", cal.value(1))
}
cargo run
Compiling closure v0.1.0 (D:\project\rust\closure)
Finished dev [unoptimized + debuginfo] target(s) in 0.66s
Running `target\debug\closure.exe`
calculating slowly...
1
calculating slowly...
2
1
測試通過