在while循环中更新可变的HashMap

时间:2019-05-21 21:12:35

标签: rust immutability borrow-checker

我正在尝试在Rust中实现Karger算法,并且在尝试在while循环中更新可变哈希图时遇到问题。

该映射已成功更新,但是在克隆后的下一次迭代中,更新后的值似乎未更改。但是,从地图中删除元素会反映在以后的迭代中。

我已经尝试调试和打印地图的值,但是事件顺序对我来说没有意义。

use itertools::Itertools;  // 0.8.0
use rand::seq::{IteratorRandom, SliceRandom}; // 0.6.5
use std::collections::HashMap;

fn contract_edge(graph: &mut HashMap<i32, Vec<i32>>, num_trials: i32) {
    let mut count = 0;

    while graph.len() > 2 && count < num_trials {
        // clone graph so I can mutate graph later
        let imut_graph = graph.clone();

        // choose random node
        let from_value = imut_graph
            .keys()
            .choose(&mut rand::thread_rng())
            .unwrap()
            .clone();
        let values = imut_graph.get(&from_value);
        let to_value = values
            .unwrap()
            .choose(&mut rand::thread_rng())
            .unwrap()
            .clone();

        let from_edges = imut_graph[&from_value].iter().clone();

        // accessing to_value in imut_graph gives error here later
        let to_edges = imut_graph[&to_value]
            .iter()
            .clone()
            .filter(|&x| *x != from_value && *x != to_value);

        let new_edges = from_edges.chain(to_edges);

        // since I am mutating the graph I thought the next time is is clone it would be updated?
        graph.insert(from_value, new_edges.map(|v| v.clone()).collect());
        graph.remove(&to_value);
        for (_key, val) in graph.iter_mut() {
            *val = val
                .iter()
                .map(|v| if v == &to_value { &from_value } else { v })
                .unique()
                .cloned()
                .collect();
        }
        count += 1;
    }
}

当我尝试访问地图时,我得到元素未找到错误,但是此时已删除的键不应该存在于矢量值中。

我坚信这是我对Rust中的(Im)可变性不了解的事情。

1 个答案:

答案 0 :(得分:1)

我不太确定您要在这里实现什么,但是根据上面的内容,我想对原始graph进行突变(因为您将其作为可变的借用给您的函数),并且您没有返回值,并且您的问题是关于对哈希映射进行突变-我假设您希望所做的更改能够反映在原始HashMap中。那么,为什么要首先克隆它?

另一方面,如果您不想更改原始对象,则不要将其作为可变借项传递,而应作为不可变的借项传递。然后在开始循环之前创建它的克隆,并在整个算法中使用该克隆的版本。

您遇到的问题之所以发生,是因为在每次迭代中,您都在克隆原始的graph而不是克隆的imut_graph,即在每次迭代中您都创建了一个新的HashMap,然后您要进行突变,然后开始一个新的循环,仍然在检查原始循环的长度,然后再次克隆原始循环。

因此,您有两个选择:

use std::collections::HashMap;

fn mutated(map: &mut HashMap<i32, Vec<i32>>) {
    map.insert(1, vec![4, 5, 6]);
}

fn cloned(map: &HashMap<i32, Vec<i32>>) -> HashMap<i32, Vec<i32>> {
    let mut map = map.clone();
    map.insert(2, vec![7, 8, 9]);
    map
}

fn main() {
    let mut map = HashMap::new();
    map.insert(0, vec![1, 2, 3]);

    println!("{:?}", cloned(&map));

    mutated(&mut map);
    println!("{:?}", map);
}

哪个会给你:

{0: [1, 2, 3], 2: [7, 8, 9]}
{0: [1, 2, 3], 1: [4, 5, 6]}