Hello folks --
I have a simplified tree node data structure with a few fields and a few methods such as:
use std::collections::hash_map;
pub struct Node {
metadata : Option<String>,
value : Option<String>,
edges: HashMap<i32, Node>,
}
impl Node {
pub(crate) fn value_mut(&mut self) -> Option<&mut String> {
self.value.as_deref_mut()
}
pub(crate) fn edges_values_mut(&mut self) -> ValuesMut<'a, i32, Node> {
self.edges.values_mut()
}
}
and I want to create an IterMut iterator to cycle through values in the tree
pub struct IterMut<'a> {
stack: Vec<&'a mut Node>
}
impl Iterator for IterMut {
type Item = &'a mut String;
fn next(&mut self) -> Option<Self::Item> {
match self.stack.pop() {
None => return None;
Some(n) => {
self.stack.extend(n.edges_values_mut())
-------------------------
|
first mutable borrow occurs here
return Some(n.value_mut());
^^^^^^^^^^^^^ second mutable borrow occurs here
}
}
}
}
I'm unable to do this without getting compiler errors about multiple mutable borrows which, understandably, follows the exclusive writer rule.
error[E0499]: cannot borrow *n
as mutable more than once at a time
One of the ways around this that I've used is changing the pub struct Node's
visibility access specifiers for fields edges and value to pub(crate). Since Rust knows that struct fields are separate and that potentially writing to one won't affect another field, it allows mutably accessing these as shown below:
pub struct Node {
pub(crate) value : Option<String>,
pub(crate) edges: HashMap<i32, Node>,
...
}
...
Some(n) => {
//updated lines
let edges = &mut n.edges;
let v = &mut n.value;
self.stack.extend(edges.values_mut())
//updated statement
return Some(v.as_deref_mut());
}
}
This works BUT it feels a bit like a hack and it compromises the encapsulation of Node by making some of its fields pub(crate)
Is there a better way to do this? I had also considered moving the Iter
module underneath Node
to be able to freely access the Node struct fields without having to change the access modifier fields.
Would appreciate any idiomatic suggestions on how to get around this concern properly