在惯用的 Rust 中更新和传递 HashMap 时如何避免密钥克隆
How to avoid key clone while updating and passing a HashMap in idiomatic Rust
我正在尝试编写一段代码来检查某个条目是否在缓存中,以及它是否不产生值。
问题是为了生成值,我想传递缓存,因为生成器可能想使用其他值,或者可能想插入自己的值。
当前代码如下:
#[derive(Default)]
pub struct R {}
#[derive(Hash, PartialEq, Eq, Clone)]
pub struct Mutation {}
fn create_mutations(s: &Mutation) -> Vec<Mutation> {
todo!();
}
fn expand(v1: &mut R, v2: &R) {
todo!();
}
pub fn rec_fn(cache: &mut HashMap<Mutation, R>, s: &Mutation) -> R {
let mut results: R = R::default();
let mutations = create_mutations(s);
for mutation in mutations {
if cache.get(&mutation).is_none() {
let r = rec_fn(cache, &mutation);
cache.insert(mutation.clone(), r);
}
let mutations_of_mutation = &cache[&mutation];
expand(&mut results, mutations_of_mutation);
}
results
}
我遇到的问题是我的 Mutation
块需要 Clone
。
我想知道我是否可以用不同的方式编写缓存获取和插入的块。
一种看起来很有希望但被借用检查器关闭的方法是:
pub fn rec_fn(cache: &mut HashMap<Mutation, R>, s: &Mutation) -> R {
let mut results: R = R::default();
let mutations = create_mutations(s);
for mutation in mutations {
let mutations_of_mutation = cache.entry(mutation).or_insert_with_key(|m| rec_fn(cache, m));
expand(&mut results, mutations_of_mutation);
}
results
}
出现错误:
error[E0500]: closure requires unique access to `*cache` but it is already borrowed
--> src/main.rs:97:78
|
97 | let mutations_of_mutation = cache.entry(mutation).or_insert_with_key(|m| rec_fn(cache, m));
| --------------------- ------------------ ^^^ ----- second borrow occurs due to use of `*cache` in closure
| | | |
| | | closure construction occurs here
| | first borrow later used by call
| borrow occurs here
我明白为什么会这样。但是有没有办法在没有克隆的情况下写这个 mutation
?
内循环有四步:
- 检查密钥是否被缓存
- 计算要插入的值(如有必要)
- 将新值插入缓存(如有必要)
- 通过引用传递缓存值
expand
第 1 步和第 4 步需要对 cache
的共享引用,而第 2 步和第 3 步需要对 cache
的可变引用。通过将这些步骤分开,可以避免这些借用之间的冲突。在第 3 步中,我们必须将 mutation
的所有权转移到 cache
,但我们可以使用 entry
API 保留对插入值的引用,而无需克隆密钥。
更新后的代码如下所示:
use std::collections::HashMap;
#[derive(Default)]
pub struct R {}
#[derive(Hash, PartialEq, Eq)]
pub struct Mutation {}
fn create_mutations(s: &Mutation) -> Vec<Mutation> {
todo!();
}
fn expand(v1: &mut R, v2: &R) {
todo!();
}
pub fn rec_fn(cache: &mut HashMap<Mutation, R>, s: &Mutation) -> R {
let mut results: R = R::default();
let mutations = create_mutations(s);
for mutation in mutations {
// 1. check if key is in cache
let cached = cache.contains_key(&mutation);
// 2. calculate value to insert, if necessary
let to_insert = if cached {
None // already cached, no need to re-calculate
} else {
Some(rec_fn(cache, &mutation))
};
// 3. fetch, or insert and keep a reference
let mutations_of_mutation = match to_insert {
None => &cache[&mutation],
Some(r) => cache.entry(mutation).or_insert(r),
};
// 4. pass value on to `expand`
expand(&mut results, mutations_of_mutation);
}
results
}
我正在尝试编写一段代码来检查某个条目是否在缓存中,以及它是否不产生值。
问题是为了生成值,我想传递缓存,因为生成器可能想使用其他值,或者可能想插入自己的值。
当前代码如下:
#[derive(Default)]
pub struct R {}
#[derive(Hash, PartialEq, Eq, Clone)]
pub struct Mutation {}
fn create_mutations(s: &Mutation) -> Vec<Mutation> {
todo!();
}
fn expand(v1: &mut R, v2: &R) {
todo!();
}
pub fn rec_fn(cache: &mut HashMap<Mutation, R>, s: &Mutation) -> R {
let mut results: R = R::default();
let mutations = create_mutations(s);
for mutation in mutations {
if cache.get(&mutation).is_none() {
let r = rec_fn(cache, &mutation);
cache.insert(mutation.clone(), r);
}
let mutations_of_mutation = &cache[&mutation];
expand(&mut results, mutations_of_mutation);
}
results
}
我遇到的问题是我的 Mutation
块需要 Clone
。
我想知道我是否可以用不同的方式编写缓存获取和插入的块。
一种看起来很有希望但被借用检查器关闭的方法是:
pub fn rec_fn(cache: &mut HashMap<Mutation, R>, s: &Mutation) -> R {
let mut results: R = R::default();
let mutations = create_mutations(s);
for mutation in mutations {
let mutations_of_mutation = cache.entry(mutation).or_insert_with_key(|m| rec_fn(cache, m));
expand(&mut results, mutations_of_mutation);
}
results
}
出现错误:
error[E0500]: closure requires unique access to `*cache` but it is already borrowed
--> src/main.rs:97:78
|
97 | let mutations_of_mutation = cache.entry(mutation).or_insert_with_key(|m| rec_fn(cache, m));
| --------------------- ------------------ ^^^ ----- second borrow occurs due to use of `*cache` in closure
| | | |
| | | closure construction occurs here
| | first borrow later used by call
| borrow occurs here
我明白为什么会这样。但是有没有办法在没有克隆的情况下写这个 mutation
?
内循环有四步:
- 检查密钥是否被缓存
- 计算要插入的值(如有必要)
- 将新值插入缓存(如有必要)
- 通过引用传递缓存值
expand
第 1 步和第 4 步需要对 cache
的共享引用,而第 2 步和第 3 步需要对 cache
的可变引用。通过将这些步骤分开,可以避免这些借用之间的冲突。在第 3 步中,我们必须将 mutation
的所有权转移到 cache
,但我们可以使用 entry
API 保留对插入值的引用,而无需克隆密钥。
更新后的代码如下所示:
use std::collections::HashMap;
#[derive(Default)]
pub struct R {}
#[derive(Hash, PartialEq, Eq)]
pub struct Mutation {}
fn create_mutations(s: &Mutation) -> Vec<Mutation> {
todo!();
}
fn expand(v1: &mut R, v2: &R) {
todo!();
}
pub fn rec_fn(cache: &mut HashMap<Mutation, R>, s: &Mutation) -> R {
let mut results: R = R::default();
let mutations = create_mutations(s);
for mutation in mutations {
// 1. check if key is in cache
let cached = cache.contains_key(&mutation);
// 2. calculate value to insert, if necessary
let to_insert = if cached {
None // already cached, no need to re-calculate
} else {
Some(rec_fn(cache, &mutation))
};
// 3. fetch, or insert and keep a reference
let mutations_of_mutation = match to_insert {
None => &cache[&mutation],
Some(r) => cache.entry(mutation).or_insert(r),
};
// 4. pass value on to `expand`
expand(&mut results, mutations_of_mutation);
}
results
}