如何同时遍历Rust HashMap并修改其某些值?

Mar*_*ter 4 rust borrow-checker

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

struct Process {
    name: String,
    weight: u32,
    children: Vec<String>,
    parent: Option<String>
}
Run Code Online (Sandbox Code Playgroud)

这些存储在中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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我不能同时具有对HashMapself.processes)和可变变量的引用,也不能具有两个可变引用。

那么,在Rust中最惯用的方法是什么?我可以看到两个选项:

  1. 在不可变引用超出范围后,一次将父/子关系复制到新的临时数据结构中,然后在第二次更新中更新Process结构。
  2. 更改我的数据结构以将“父”放入其自己的HashMap中。

还有第三种选择吗?

tre*_*tcl 5

是的,您可以HashMap使用来为的值授予内部可变性RefCell

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());
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

borrow_mut如果孩子已向借用,则在运行时会感到恐慌borrow。如果进程是其自己的父进程,则会发生这种情况(大概永远不会发生,但是在功能更强大的程序中,您希望提供有意义的错误消息,而不仅仅是恐慌)。

我发明了一些名称,并做了一些小改动(除了特别指出的以外),以使该代码得以编译。值得注意的是,p.name.clone()制作的完整副本p.name。这是必需的,因为nameparent都由拥有String