How would one restructure this function that does a depth-first search and returns the parent of the matching node?
I know that variations of this problem have come up very often (e.g. Multiple mutable borrows when generating a tree structure with a recursive function in Rust, Mut borrow not ending where expected), but I still can’t figure out how to modify it to work. I have tried variations using slices, std::mem::drop
, and adding lifetime parameters where 'a: 'b
but I still haven’t figured out write it without conditionally mutably borrowing a variable in one branch and then using the variable in another branch.
#[derive(Clone, Debug)]
struct TreeNode {
id: i32,
children: Vec<TreeNode>,
}
// Returns a mutable reference to the parent of the node that matches the given id.
fn find_parent_mut<'a>(root: &'a mut TreeNode, id: i32) -> Option<&'a mut TreeNode> {
for child in root.children.iter_mut() {
if child.id == id {
return Some(root);
} else {
let descendent_result = find_parent_mut(child, id);
if descendent_result.is_some() {
return descendent_result;
}
}
}
None
}
fn main() {
let mut tree = TreeNode {
id: 1,
children: vec![TreeNode {
id: 2,
children: vec![TreeNode {
id: 3,
children: vec![],
}],
}],
};
let a: Option<&mut TreeNode> = find_parent_mut(&mut tree, 3);
assert_eq!(a.unwrap().id, 2);
}
error[E0499]: cannot borrow `*root` as mutable more than once at a time
--> src/main.rs:11:25
|
9 | for child in root.children.iter_mut() {
| ------------- first mutable borrow occurs here
10 | if child.id == id {
11 | return Some(root);
| ^^^^ second mutable borrow occurs here
...
20 | }
| - first borrow ends here
Here it is with @huon’s suggestions and continued compiler errors:
fn find_parent_mut<'a>(root: &'a mut TreeNode, id: i32) -> Option<&'a mut TreeNode> {
for child in root.children {
if child.id == id {
return Some(root);
}
}
for i in 0..root.children.len() {
let child: &'a mut TreeNode = &mut root.children[i];
let descendent_result = find_parent_mut(child, id);
if descendent_result.is_some() {
return descendent_result;
}
}
None
}
error[E0507]: cannot move out of borrowed content
--> src/main.rs:9:18
|
9 | for child in root.children {
| ^^^^ cannot move out of borrowed content
error[E0499]: cannot borrow `root.children` as mutable more than once at a time
--> src/main.rs:15:44
|
15 | let child: &'a mut TreeNode = &mut root.children[i];
| ^^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first mutable borrow occurs here
...
22 | }
| - first borrow ends here
I managed to have it working this way:
First in you second attempt, you wrote
for child in root.children
instead of thefor child in &mut root.children
(note the missing&mut
),which caused root.children to be consumed by the loop instead of just iterated over, hence thecannot move out of borrowed content
error.I also folded it in a more iterator-ich way, using the
any(..)
function.For the second loop, I'm not exactly sure what was going on, by apparently binding the references to variables was confusing the borrow-checker. I removed any temporary variable, and now it compiles.
One can do this in a single pass by doing the search, recording some data from it, and then computing the return value efficiently outside the loop:
This avoids the issues caused by conditionally returning mutable variables (which is the problems you and my answer below were meeting) by completely avoiding returns inside the inner loop.
Old/incorrect answer:
I'm unable to test my suggestions right now, but I believe the best way to solve this is to return
root
outside the loop.This (hopefully) ensures that
root
isn't borrowed viachildren
when it is being manipulated.However I suspect the early return inside the main loop may interfere, in which case one might just have to fall back to integers and indexing: