如何同时迭代Rust HashMap并修改其某些值?

时间:2017-12-10 08:20:18

标签: rust borrow-checker

我正在尝试今年Rust中的Code出现,作为学习语言的一种方式。我已将输入(从第7天开始)解析为以下结构:

struct Process {
    name: String,
    weight: u32,
    children: Vec<String>,
    parent: Option<String>
}

这些存储在HashMap<String, Process>中。现在我想迭代地图中的值并根据我在父级“子”向量中找到的内容更新父值。

什么行不通是

for p in self.processes.values() {
    for child_name in p.children {
        let mut child = self.processes.get_mut(child_name).expect("Child not found.");
        child.parent = p.name;
    }
}

我不能同时提及HashMapself.processes)和非可变引用,或两个可变引用。

那么,在Rust中实现这一目标的最惯用方法是什么?我能看到的两个选项是:

  1. 在一次传递中将父/子关系复制到一个新的临时数据结构中,然后在不可变引用超出范围后,在第二次传递中更新Process结构。
  2. 更改我的数据结构,将“父”放在自己的HashMap中。
  3. 有第三种选择吗?

1 个答案:

答案 0 :(得分:4)

是的,您可以使用RefCellHashMap的值授予内部可变性:

struct ProcessTree {
    processes: HashMap<String, RefCell<Process>>,  // change #1
}

impl ProcessTree {
    fn update_parents(&self) {
        for p in self.processes.values() {
            let p = p.borrow();                    // change #2
            for child_name in &p.children {
                let mut child = self.processes
                    .get(child_name)               // change #3
                    .expect("Child not found.")
                    .borrow_mut();                 // change #4
                child.parent = Some(p.name.clone());
            }
        }
    }
}
如果孩子已经借用borrow_mut,那么

borrow会在运行时发生恐慌。如果一个进程是它自己的父进程(这可能永远不会发生,但在一个更强大的程序中,你想要提供一个有意义的错误信息,而不仅仅是恐慌),就会发生这种情况。

我发明了一些名称并进行了一些小改动(除了专门指出的那些)以使这段代码编译。值得注意的是,p.name.clone()制作了p.name的完整副本。这是必要的,因为nameparent都归String所有。