我正在尝试在 Rust 中实现惰性构造/记忆化评估/缓存习惯用法。
有一个外部类型,它有一堆数据和一个访问器方法。访问器方法需要返回缓存的计算(如果有)或计算它并将返回值存储在映射中以供以后使用。缓存的值不需要引用外部值,因此不存在循环引用问题;但它确实需要访问外部值的数据才能构建自身。
这是一个未通过 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() {}
具体错误是:
error[E0502]: cannot borrow `self` as immutable because `self.map` is also borrowed as mutable
--> src/main.rs:24:29
|
22 | self.map
| -------- mutable borrow occurs here
23 | .entry(key)
24 | .or_insert_with(|| ContainedThing::create(&self))
| ^^ ---- borrow occurs due to use of `self` in closure
| |
| immutable borrow occurs here
25 | }
| - mutable borrow ends here
我很难找到实现这个习惯用法的好方法。我尝试了模式匹配的返回值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
;也就是说,没有毫无意义的模式匹配。
我的笨拙代码是可以实现的最好的吗?