【问题标题】:How to avoid key clone while updating and passing a HashMap in idiomatic Rust如何在惯用 Rust 中更新和传递 HashMap 时避免密钥克隆
【发布时间】:2022-01-14 23:53:05
【问题描述】:

我正在尝试编写一段代码来检查一个条目是否在缓存中,以及它是否不产生值。

问题是为了产生值,我想传递缓存,因为生产者可能想要使用其他值,或者可能想要插入自己的值。

当前代码如下:

#[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的情况下写这个?

【问题讨论】:

    标签: rust idioms


    【解决方案1】:

    内循环有四个步骤:

    1. 检查密钥是否被缓存
    2. 计算要插入的值(如有必要)
    3. 将新值插入缓存(如有必要)
    4. 通过引用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
    }
    

    (playground link)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-23
      • 2020-06-09
      • 2021-11-08
      • 2018-03-27
      • 1970-01-01
      • 2010-11-30
      • 1970-01-01
      • 2019-10-27
      相关资源
      最近更新 更多