【问题标题】:How can I simultaneously iterate over a Rust HashMap and modify some of its values?如何同时迭代 Rust HashMap 并修改它的一些值?
【发布时间】:2017-12-10 08:20:18
【问题描述】:

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

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

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

没用的是

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;
    }
}

我不能同时拥有对HashMap (self.processes) 的可变引用和非可变引用,或者两个可变引用。

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

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

还有第三种选择吗?

【问题讨论】:

  • 在我看来很明显self.processesHashMap&lt;String, Process&gt;
  • 我可能会选择Vec&lt;Process&gt;,并使用usize 作为其他条目的参考。可以使用两遍方法:首先构建HashMap&lt;String, usize&gt; 并填充name + weight,在第二遍中填充childrenparent。很多可能性:)
  • @Stefan:嗯,我只是不喜欢使用不完整的代码;不过,我想这是一个可以推理的问题。
  • 另一种选择是将 parent 的类型更改为 RefCell&lt;Option&lt;String&gt;&gt; - 即使您只有一个 const 引用,这也将允许更改父级。
  • 另请注意,您需要克隆字符串以将其从 p.name 复制到 child.parent - 您不能将其移出借用的上下文(即使可以)。

标签: rust borrow-checker


【解决方案1】:

是的,您可以使用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 借用,borrow_mut 将在运行时恐慌。如果一个进程是它自己的父进程,就会发生这种情况(这可能永远不会发生,但在一个更健壮的程序中,您希望给出一个有意义的错误消息,而不是只是恐慌)。

我发明了一些名称并进行了一些小的更改(除了特别指出的那些)以使此代码能够编译。值得注意的是,p.name.clone() 制作了p.name 的完整副本。这是必要的,因为nameparent 都属于Strings。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-30
    • 2021-12-18
    • 2012-06-15
    • 2023-01-23
    • 1970-01-01
    • 2011-10-10
    • 2020-08-24
    • 2012-12-08
    相关资源
    最近更新 更多