Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
501 views
in Technique[技术] by (71.8m points)

borrow checker - How can I simultaneously iterate over a Rust HashMap and modify some of its values?

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

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.

What doesn't work is

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

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

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

  1. Copy the parent/child relationships into a new temporary data structure in one pass, and then update the Process structs in a second pass, after the immutable reference is out of scope.
  2. Change my data structure to put "parent" in its own HashMap.

Is there a third option?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 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).

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...