gpt4 book ai didi

hashmap - 如何在 Rust 中懒惰地创建其构造使用 self 的映射条目

转载 作者:行者123 更新时间:2023-11-29 08:00:32 27 4
gpt4 key购买 nike

我正在尝试在 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,但仍然与借用检查器发生冲突:匹配 表达式最终也借用了 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)并且似乎需要比应该需要的更多的查找和复制。理想情况下,我想

  1. 只支付一次查找哈希条目的费用。 entry(),做对了,应该在找不到时跟踪插入位置。
  2. 减少新构造值的副本数。这可能是不可行的,理想情况下我有一个就地结构。
  3. 避免使用unwrap;没有无意义的模式匹配,也就是说。

我的笨拙代码是可以实现的最好的代码吗?

最佳答案

答案是它具体取决于您需要在 or_insert_with 闭包中访问哪个状态。问题是 or_insert_with 绝对不能访问 map 本身,因为入口 api 需要对 map 进行可变借用。

如果ContainedThing::create 所需的只是 map 的大小,那么您只需提前计算 map 大小即可。

impl Thing {
pub fn get(&mut self, key: i32) -> &ContainedThing {
let map_size = self.map.len();
self.map.entry(&key).or_insert_with(|| {
// The call to entry takes a mutable reference to the map,
// so you cannot borrow map again in here
ContainedThing::create(map_size)
})
}
}

不过,我认为这个问题的精神更多地是关于一般策略,所以让我们假设 Thing 中还有一些其他状态也需要创建 ContainedThing

struct Thing {
map: HashMap<i32, ContainedThing>,
some_other_stuff: AnotherType, //assume that this other state is also required in order to create ContainedThing
}

impl Thing {
pub fn get(&mut self, key: i32) -> &ContainedThing {
//this is the borrow of self
let Thing {
ref mut map,
ref mut some_other_stuff,
} = *self;

let map_size = map.len();
map.entry(key).or_insert_with(|| {
// map.entry now only borrows map instead of self
// Here you're free to borrow any other members of Thing apart from map
ContainedThing::create(map_size, some_other_stuff)
})
}
}

这是否真的比手动检查 if self.map.contains_key(&key) 的其他解决方案更干净尚有争议。不过,我倾向于采用解构策略,以允许借用 self 的特定成员而不是整个结构。

关于hashmap - 如何在 Rust 中懒惰地创建其构造使用 self 的映射条目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38999422/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com