我試圖在Rust中實現一個懶惰構造/ memoized評估/緩存成語。如何懶洋洋地創建地圖條目,其構建在Rust中使用自我
它的工作方式是有一個外部類型,它有一堆數據和一個存取方法。訪問器方法需要返回緩存計算(如果有),或者計算它並將返回值存儲在映射中以供將來使用。緩存的值不需要引用外部值,因此不存在循環引用問題;但它確實需要訪問外部值的數據才能構建自己。
這裏的,其不通過鏽蝕的借檢查一個完整的例子:
use std::collections::HashMap;
pub struct ContainedThing {
count: usize,
}
impl ContainedThing {
fn create(thing: &Thing) -> ContainedThing {
// create uses an arbitrary number of attributes from Thing
// it doesn't keep any references after returning though
let count = thing.map.len();
ContainedThing { count: count }
}
}
struct Thing {
map: HashMap<i32,ContainedThing>,
}
impl Thing {
pub fn get(&mut self, key: i32) -> &ContainedThing {
self.map.entry(key).or_insert_with(|| ContainedThing::create(&self))
}
}
fn main() {
}
特定的錯誤是這樣的:
test.rs:20:44: 20:46 error: cannot borrow `self` as immutable because `self.map` is also borrowed as mutable [E0502]
test.rs:20 self.map.entry(key).or_insert_with(|| ContainedThing::create(&self))
^~
test.rs:20:9: 20:17 note: mutable borrow occurs here
test.rs:20 self.map.entry(key).or_insert_with(|| ContainedThing::create(&self))
^~~~~~~~
test.rs:21:5: 21:6 note: mutable borrow ends here
test.rs:21 }
^
test.rs:20:71: 20:75 note: borrow occurs due to use of `self` in closure
test.rs:20 self.map.entry(key).or_insert_with(|| ContainedThing::create(&self))
^~~~
我有一個真的很難搞清楚的好方法實施這個成語。我嘗試了get()
而不是entry()
API的模式匹配返回值,但仍然與借用檢查器相沖突:match
表達式也最終借用了self
。
我可以重寫get
這樣的:
pub fn get(&mut self, key: i32) -> &ContainedThing {
if !self.map.contains_key(&key) {
let thing = ContainedThing::create(&self);
self.map.insert(key, thing);
}
self.map.get(&key).unwrap()
}
但是這是很醜陋(看那個unwrap
),似乎需要更多的查找和複印的速度比應該是必要的。理想情況下,我想
- 只支付一次查找哈希項的成本。
entry()
,做得不錯,應該在未找到時跟蹤插入位置。 - 減少新構建值的拷貝數。這可能是不可行的,理想情況下我有一個就地建設。
- 避免使用
unwrap
;沒有無意義的模式匹配,也就是說。
我的笨拙代碼是最好的可以實現嗎?
會是http://stackoverflow.com/q/30681468/155423或類似的副本。你不能傳遞'self',因爲它正在被改變。如果'entry'給你提供了'HashMap'的引用,那麼你可能會進行更多的查找。 – Shepmaster
@Shepmaster:我會說它略有不同,因爲在lambda中使用了引用。這裏的執行順序很好:在執行lambda時,哈希映射被凍結。 –
是否需要借用'&Thing',或者將以前的計算(以下'.len()')從「&Thing」中提取出來,然後「解壓」那麼你會避免借用,因此你可以使用'or_insert_with'。 –