> My examples code above may not be persuasive to experienced Rustaceans. They might argue that the snippets don't show there is any real ergonomic problem, because the solutions to make the snippets compile are completely trivial. In the last example, I could just derive Clone + Copy for Id.
No, you could use destructuring. This doesn't work for all cases but it does for your examples without needing to derive copy or clone. Here's a more complex but also compelling example of the problem:
struct Graph {
nodes: BTreeMap,
}
struct Node {
edges: Vec,
}
impl Graph {
fn visit_mut(&mut self, visit: impl Fn(&mut Node, &mut Node)) {
let mut visited = BTreeSet::new();
let mut stack = vec![0];
while let Some(id) = stack.pop() {
if !visited.insert(id) { continue; }
let curr = self.nodes.get_mut(&id);
for id in source.edges.clone() {
let next = self.nodes.get_mut(&id);
visit(curr, next);
stack.push(id);
}
}
}
}
We're doing everything in the "Rust" way here. We're using IDs instead of pointers. We're cloning a vec even if it's a bit excessive. But the bigger problem is we actually _do_ need to have multiple mutable references to two values owned by a collection that we know don't transitively reference the collection. We need to wrap these in an RefCell or UnsafeCell and unsafe { } block to actually get mutable references to the underlying data to correctly implement visit_mut().
This is a problem that shows up all the time when using collections, which Rust encourages within the ecosystem.