The most straightforward approach to graphs (which is what you have if you have back pointers) is to use indices:
// just for clarity
#[derive(Copy, Clone)] // probably lots more...
struct NodeId(usize);
struct Graph<T> {
nodes: Vec<T>,
}
impl<T> Graph<T> {
pub fn new() -> Self {
Self { nodes: vec![] }
}
// no remove or it gets way more complicated!
pub fn add(&mut self, value: T) -> NodeId {
let result = NodeId(self.nodes.len());
self.nodes.push(value);
result
}
pub fn get(&self, id: NodeId) -> Option<&T> {
self.nodes.get(id)
}
// ...
}
struct TreeNode<T> {
parent: Option<NodeId>,
value: T,
children: Vec<NodeId>,
}
struct Tree<T> {
graph: Graph<TreeNode<T>>,
root: NodeId,
}
impl<T> Tree<T> {
pub fn new(root_value: T) -> Self {
let graph = Graph::new();
graph.add(TreeNode {
parent: None,
value: root_value,
children: vec![],
});
Self { graph, root }
}
pub fn root(&self) -> NodeId {
self.root
}
pub fn get(id: NodeId) -> Option<&TreeNode<T>> {
self.graph.get(id)
}
// ...
}
You should certainly be looking to use something purpose built here like petgraph if you go this route, it handles a lot of book-keeping details for you as well as providing a rich set of algorithms.
The easier option is as mentioned to just not have the back pointers in the first place:
struct Tree<T> {
value: T,
children: Vec<Tree<T>>,
}
impl<T> Tree<T> {
// ...
pub fn visit(&self, mut visitor: impl FnMut(&T, &[(&T, usize)])) {
let mut stack = vec![(self, 0)];
let mut path = vec![];
visitor(&self.value, &path);
while let Some((node, index)) = stack.pop() {
if let Some(child) = node.children.get(index) {
stack.push((node, index + 1));
stack.push((child, 0));
path.push((&node.value, index));
visitor(&child.value, &path)
} else {
path.pop();
}
}
assert!(path.is_empty());
}
}
fn main() {
let tree = Tree {
value: "A",
children: vec![
Tree {
value: "B",
children: vec![],
},
Tree {
value: "C",
children: vec![Tree {
value: "D",
children: vec![],
}],
},
Tree {
value: "E",
children: vec![],
},
],
};
tree.visit(|value, path| {
println!("{value}: {path:?}");
});
}
A: []
B: [("A", 0)]
C: [("A", 1)]
D: [("A", 1), ("C", 0)]
E: [("A", 2)]
Hope you remembered your algorithms class!