如何同时迭代 Rust HashMap 并修改其某些值? [英] How can I simultaneously iterate over a Rust HashMap and modify some of its values?

查看:91
本文介绍了如何同时迭代 Rust HashMap 并修改其某些值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying Advent of Code in Rust this year, as a way of learning the language. I've parsed the input (from day 7) into the following structure:

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

这些存储在 HashMap 中.现在我想迭代地图中的值并根据我在父级的子级"向量中找到的值更新父级值.

These are stored in a HashMap<String, Process>. Now I want to iterate over the values in the map and update the parent values, based on what I find in the parent's "children" vector.

行不通的是

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) 的可变引用和非可变引用,或两个可变引用.

I can't have both a mutable reference to the HashMap (self.processes) and a non-mutable reference, or two mutable references.

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

So, what is the most idiomatic way to accomplish this in Rust? The two options I can see are:

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

还有第三种选择吗?

推荐答案

是的,您可以使用 RefCell:

Yes, you can grant internal mutability to the HashMap's values using 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());
            }
        }
    }
}

borrow_mut 将在运行时发生恐慌,如果孩子已经通过 borrow 借用了.如果一个进程是它自己的父进程,就会发生这种情况(这可能永远不会发生,但在更健壮的程序中,您希望给出有意义的错误消息,而不仅仅是恐慌).

borrow_mut will panic at runtime if the child is already borrowed with borrow. This happens if a process is its own parent (which should presumably never happen, but in a more robust program you'd want to give a meaningful error message instead of just panicking).

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

I invented some names and made a few small changes (besides the ones specifically indicated) to make this code compile. Notably, p.name.clone() makes a full copy of p.name. This is necessary because both name and parent are owned Strings.

这篇关于如何同时迭代 Rust HashMap 并修改其某些值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆