13.7 Iterating Over Complex Structures: Binary Tree Example
Iterators are not limited to linear sequences like vectors or arrays. They can encapsulate the traversal logic for more complex data structures, such as trees or graphs, providing a standard Iterator
interface for consuming code.
Here’s an example of implementing an in-order traversal iterator for a simple binary tree. We use Rc<RefCell<TreeNode<T>>>
to handle shared ownership and potential mutation (though mutation isn’t used in this traversal itself), which is common in graph-like structures in Rust where nodes might be reachable via multiple paths.
use std::rc::Rc; use std::cell::RefCell; use std::collections::VecDeque; // Using VecDeque as a stack // Node definition using shared ownership via Rc and interior mutability via RefCell type TreeNodeLink<T> = Option<Rc<RefCell<TreeNode<T>>>>; #[derive(Debug)] struct TreeNode<T> { value: T, left: TreeNodeLink<T>, right: TreeNodeLink<T>, } impl<T> TreeNode<T> { // Helper to create a new node wrapped in Rc<RefCell<...>> fn new(value: T) -> Rc<RefCell<Self>> { Rc::new(RefCell::new(TreeNode { value, left: None, right: None, })) } } // Iterator struct for in-order traversal struct InOrderIter<T: Clone> { // Require T: Clone to yield owned values // Stack holds nodes waiting to be visited (after their left subtree is done) stack: VecDeque<Rc<RefCell<TreeNode<T>>>>, // Current node pointer, used to navigate down left branches current: TreeNodeLink<T>, } impl<T: Clone> InOrderIter<T> { // Creates a new iterator starting traversal from the root fn new(root: TreeNodeLink<T>) -> Self { let mut iter = InOrderIter { stack: VecDeque::new(), current: root, }; // Initialize by pushing the left spine onto the stack iter.push_left_spine(); iter } // Helper: Pushes the current node and all its left children onto the stack. // Sets `self.current` to None after finishing. fn push_left_spine(&mut self) { while let Some(node) = self.current.take() { // Take ownership of current link self.stack.push_back(node.clone()); // Push node onto stack // Prepare to move left: borrow immutably to get left child link let left_link = node.borrow().left.clone(); self.current = left_link; // Update current to the left child } } } impl<T: Clone> Iterator for InOrderIter<T> { type Item = T; // Yield owned copies of node values fn next(&mut self) -> Option<Self::Item> { // If current is Some, it means we just moved right from a popped node. // Push the new current node and its left spine onto the stack. if self.current.is_some() { self.push_left_spine(); } // Pop the next node from the stack (this is the next in-order node) if let Some(node_to_visit) = self.stack.pop_back() { // Borrow node to access value and right child let node_ref = node_to_visit.borrow(); let value_to_return = node_ref.value.clone(); // Clone value for return // Prepare for the *next* call: move to the right child. // The next call to `next()` will handle pushing this right child // and its left spine (if it exists) via `push_left_spine`. self.current = node_ref.right.clone(); Some(value_to_return) } else { // Stack is empty and current is None -> Traversal complete None } } } // Add a convenience method to initiate the iteration from a root node impl<T: Clone> TreeNode<T> { // Creates the in-order iterator for a tree rooted at `link` fn in_order_iter(link: TreeNodeLink<T>) -> InOrderIter<T> { InOrderIter::new(link) } } fn main() { // Build a simple binary search tree: // 4 // / \ // 2 6 // / \ / \ // 1 3 5 7 let root = TreeNode::new(4); let node1 = TreeNode::new(1); let node3 = TreeNode::new(3); let node5 = TreeNode::new(5); let node7 = TreeNode::new(7); let node2 = TreeNode::new(2); node2.borrow_mut().left = Some(node1.clone()); node2.borrow_mut().right = Some(node3.clone()); let node6 = TreeNode::new(6); node6.borrow_mut().left = Some(node5.clone()); node6.borrow_mut().right = Some(node7.clone()); root.borrow_mut().left = Some(node2.clone()); root.borrow_mut().right = Some(node6.clone()); // Use the iterator and collect the results println!("Tree nodes (in-order traversal):"); let traversal: Vec<i32> = TreeNode::in_order_iter(Some(root)).collect(); println!("{:?}", traversal); // Expected: [1, 2, 3, 4, 5, 6, 7] assert_eq!(traversal, vec![1, 2, 3, 4, 5, 6, 7]); // Example of using the iterator step-by-step with a single node tree let root_single = TreeNode::new(10); let mut iter_manual = TreeNode::in_order_iter(Some(root_single)); assert_eq!(iter_manual.next(), Some(10)); assert_eq!(iter_manual.next(), None); assert_eq!(iter_manual.next(), None); // Fused behavior }
This example demonstrates how the Iterator
trait can encapsulate complex stateful traversal logic (managing a stack and current node pointer for tree traversal), exposing it through the simple, standard next()
interface familiar to users of standard collection iterators. The T: Clone
bound is necessary here because the iterator only has shared references (Rc<RefCell<...>>
) to the nodes but needs to yield owned T
values. An alternative design could yield references or require T: Copy
.