Yeah, it pretty much is.
I'm not sure I understand your complete vision; your playground has iterators that return trees, but then WithChildrenMut::from
just uses a single tree and discards the rest of the iterator (it could have just taken a &'a mut ProcTree
. Back to that in a moment...
// get two mutable references out of the
// iterator, potentially children of processes which are still in the iterator.
let p1: &mut Process = iter.next().unwrap();
let p2: &mut Process = iter.next().unwrap();
This part is problematic, if you want to actually hold on to a &mut ThingThatOwnsProcess
. The only way to hold on to that while handing out children is to reborrow. Reborrowing means you need a lending iterator, not a std::Iterator
. However you also want multiple processes at a time, so now we'd be looking at something like get_disjoint_mut
... but even that can only get you so far:
// this should recursively iterate through the children of all
// the processes left in the iterator... meaning potentially the processes p1 and p2
let iter2 = iter.with_children();
// this may be the same Process as p1
let p3: &mut Process = iter.next().unwrap();
It won't really help with these, because...
// Should fail to compile here and only here?
p1.mutate();
...borrow checking needs to ensure that active &mut _
are exclusive references at compile time. (And it really does mean exclusive: an aliased active &mut _
is UB even if no actual mutation happens through it. Think of &mut _
as "mutually exclusive".)
And yeah, hidden behind that "active" qualifier is all that reborrowing stuff.
OK, back to your playground. If you only need the children of one tree, we can make the iterator portion of your playground work. At least if I'm understanding correctly.
I didn't change your recursive usize
collector. But instead of holding onto the &mut ProcTree
, I did this:
pub struct WithChildrenMut<'a> {
// tree: &'a mut ProcTree,
nodes: Vec<Option<&'a mut ProcNode>>,
visit: hash_set::IntoIter<usize>
}
// Then in WithChildrenMut::from
let tree = iter.into_tree_mut();
let nodes: Vec<Option<&mut ProcNode>>
= tree.tree.iter_mut().map(Some).collect();
WithChildrenMut {
nodes,
visit: matched.into_iter(),
}
Now you can implement Iterator
like so:
fn next<'b>(self: &'b mut WithChildrenMut<'a>) -> Option<&'a mut ProcNode> {
let idx = self.next_idx()?;
self.nodes[idx].take()
}
(It could also work with multiple &mut ProcTree
, but I didn't attempt such. If nothing else you could use a flat_map
approach.)
The difference from before and the intermediate discussion above is that we're not trying to hold on to the parent &'a mut ProcTree
while we hand out its children. Instead we've effectively converted it into a Vec
of disjoint &'a mut ProcNode
. (There are other, potentially better choices here; I arrived at this point by making minimal changes.)
However, giving up on holding the &mut ProcTree
may clash with your larger design. I had to toss out most of the implementations of your other traits -- methods which more or less amount to "I am or am holding onto a &[mut] ProcTree
".
impl<'a> ProcTreeIterPrivate<'a> for WithChildrenMut<'a> {
fn tree(&self) -> &ProcTree {
unimplemented!() // self.tree
}
fn into_tree(self) -> &'a ProcTree {
unimplemented!() // self.tree
}
fn next_idx(&mut self) -> Option<usize> {
self.visit.next()
}
}
impl<'a> ProcTreeIterMutPrivate<'a> for WithChildrenMut<'a> {
fn tree_mut(&mut self) -> &mut ProcTree {
unimplemented!() // self.tree
}
fn into_tree_mut(self) -> &'a mut ProcTree {
unimplemented!() // self.tree
}
}
If you had some ProcTree
identifier that could be used to recreate a &mut ProcTree
later on, perhaps that's an area to explore. But recreating the parent &mut ProcTree
would conflict with any of its &mut ProcNode
s still being in use.
The standard book, sadly, will not help on this topic (and may hurt). The Brown version of chapter 4 gives a model closer to reality. It has its own problems, but their permission model is much more accurate than the standard book.