I have the following (trimmed down) Rust code:
use std::collections::HashMap;
struct Node {
weight: f64,
outbound: f64,
}
struct Graph {
edges: HashMap<u32, HashMap<u32, f64>>,
nodes: HashMap<u32, Node>,
}
impl Graph {
fn mutate(&mut self) {
for (key, value) in self.nodes.iter() {
if self.edges.contains_key(key) {
for (target, weight) in self.edges[key].iter() {
self.nodes.entry(*target).or_insert(Node::new()).weight;
}
}
}
}
}
However, I cannot get the code to compile due to Rust ownership rules (playground):
graph.rs:88:25: 88:35 error: cannot borrow `self.nodes` as mutable because it is also borrowed as immutable [E0502]
graph.rs:88 self.nodes.entry(*target).or_insert(Node::new()).weight;
^~~~~~~~~~
If I change the first loop to use HashMap::iter_mut()
instead, I get a different error (playground):
graph.rs:88:25: 88:35 error: cannot borrow `self.nodes` as mutable more than once at a time [E0499]
graph.rs:88 self.nodes.entry(*target).or_insert(Node::new()).weight;
^~~~~~~~~~
How can this kind of nested loop with mutations be achieved in Rust?
You can't insert or delete elements in a data structure while you are iterating over it.
As far as I know Rust iterators don't support modification either (like Java iterators' remove()
).
So you are left with these options:
If there are only a few modifications, you can collect them and execute them after the iteration is finished.
If most of the data structure is modified, or if it is small enough that the overhead of copying doesn't matter, you can create a new, modified data structure that replaces the original one after the iteration. This is usually the idiomatic solution, using higher-order functions on iterators like map
, flat_map
or filter
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With