首页 > 解决方案 > 根据同一个Hashmap中的另一个值处理Hashmap中的值

问题描述

HashMap在我的代码中有一个查找表,映射 ID <-> 数据。

我需要根据我的 ID 查找一些数据(我们称之为数据 A),然后读取内容。根据内容中的条目,我需要在同一个查找表中查找另一个值,读取这些数据,并进行一些计算,更新我的原始数据 A。

这是一个最小的工作示例:

操场

use std::collections::HashMap;

struct MyData {
    id: i32,
    result: i32,
    complex_data: Vec<i32>
}

impl MyData {
    fn new(id: i32) -> Self {
        MyData {
            id,
            result: 0,
            complex_data: Vec::new()
        }
    }
}

fn main() {
    let mut lookup_table = HashMap::new();

    // init data
    lookup_table.insert(1, MyData::new(1));
    lookup_table.insert(2, MyData::new(2));
    lookup_table.insert(3, MyData::new(3));
    lookup_table.insert(4, MyData::new(4));

    // process data based on an ID. In this example, hard coded as "1"
    if let Some(data) = lookup_table.get_mut(&1) {
        // process each entry
        for c in data.complex_data.iter() {
            // lookup some more values based the entry
            if let Some(lookup_data) = lookup_table.get(c) {
                                               //^^^^^^^^^^^^^^^^^^^ - cannot borrow `lookup_table` as immutable
                // do some calculation and store result
                data.result = lookup_data.result + 42;      // random calculation as an example
            }
        }
    }

    println!("Hello, world!");
}

发生错误是因为我似乎借lookup_table了两次。据我了解,编译器担心我的第二次查找也会查找 ID = 1,这意味着我同时拥有 DataID = 1 的可变引用和 DataID = 1 的不可变引用。

但是,我对此很好,因为我的第二次读取是不可变的,而且整个事情都是单线程的,所以我不担心任何竞争条件。

如何重构我的代码以使 Rust 编译器满意,同时实现我的功能?

标签: rust

解决方案


我认为您可以通过在 if 语句的第一部分使用不可变借用执行所有读取、将计算结果保存到临时向量中并在第二部分使用可变借用执行所有写入来解决此问题。请参阅下面的代码。

// process data based on an ID. In this example, hard coded as "1"
if let Some(data) = lookup_table.get(&1) {
    let mut results = Vec::new();
    // process each entry
    for c in data.complex_data.iter() {
        // lookup some more values based the entry
        if let Some(lookup_data) = lookup_table.get(c) {                                             
            // do some calculation and store result
            results.push(lookup_data.result);
        }
    }
    
    let data = lookup_table.get_mut(&1).unwrap();
    for v in results {
        data.result = v + 42;
    }
}

后一个赋值data遮蔽了前一个并结束了不可变借用的生命周期。

操场


推荐阅读