This is a follow-up to Basic binary tree manipulation in Rust, where Shepmaster suggested that I implement the Iterator
trait for the binary tree.
This ended up being less trivial than I thought.
My original data structure design
was that the iterator would simply contain
a vector of binary tree nodes representing the current stack frame
of the in-order traversal, had it been implemented recursively.
However, I realized (after banging my head against the borrow checker)
that this is not possible:
it's not possible for both Branch(left, right)
and left
to be owned by the vector,
because Branch(left, right)
itself owns left
.
So instead we only track the right-facing subtrees (because those are all that's necessary to continue the "recursion"), in addition to the actual value of the left-most node.
Other questions:
- I had originally written
new_branch
andnew_leaf
inside theimpl<T> BTree<T>
block. This worked, but I wasn't able touse BTree::new_branch
to access them without qualification: I got E0432 "There is nonew_branch
inBTree
." I even tried making it apub fn
, but that didn't help. What's the proper way to have this namespaced and yetuse
able? - Is the shadowing of
btree
in the test case considered bad form? - Stylistically, when are
expr
andreturn expr;
preferred? Is there a technical difference? - Is
panic!
the best way to handle invariant violations?
/// A binary tree with data only at the leaves.
#[derive(Debug, PartialEq, Clone)]
enum BTree<T> {
Leaf(T),
Branch(Box<BTree<T>>, Box<BTree<T>>),
}
/// This makes it slightly less painful to put things in boxes.
fn new_branch<T>(left: BTree<T>, right: BTree<T>) -> BTree<T> {
BTree::Branch(Box::new(left), Box::new(right))
}
/// For consistency with `new_branch`.
fn new_leaf<T>(elem: T) -> BTree<T> {
BTree::Leaf(elem)
}
#[test]
fn test_btree_creation() {
new_leaf(10);
let branch: BTree<i32> = new_branch(new_leaf(15), new_leaf(20));
new_branch(branch.clone(), new_leaf(30));
assert_eq!(branch, branch.clone());
}
impl<T> BTree<T> {
// presumably there are useful methods here, like depth(&self) -> u32, etc.
}
impl<T> IntoIterator for BTree<T> {
type Item = T;
type IntoIter = BTreeIterator<T>;
fn into_iter(self) -> BTreeIterator<T> {
BTreeIterator::new(self)
}
}
/// Iterator type for a binary tree.
/// This is a generator that progresses through an in-order traversal.
struct BTreeIterator<T> {
right_nodes: Vec<BTree<T>>,
current_node: Option<BTree<T>>,
}
impl<T> BTreeIterator<T> {
fn new(node: BTree<T>) -> BTreeIterator<T> {
let mut iter = BTreeIterator { right_nodes: vec![], current_node: None };
iter.add_left_subtree(node);
return iter;
}
/// Consume a binary tree node, traversing its left subtree and
/// adding all branches to the right to the `right_nodes` field
/// while setting the current node to the left-most child.
fn add_left_subtree(&mut self, root: BTree<T>) {
let mut node: BTree<T> = root;
while let BTree::Branch(left, right) = node {
self.right_nodes.push(*right);
node = *left;
}
self.current_node = Some(node);
}
}
impl<T> Iterator for BTreeIterator<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
// Get the item we're going to return.
let result = self.current_node.take().map(|node|
match node {
BTree::Leaf(n) => n,
_ => { panic!("invariant violation: expected Leaf") },
});
// Now add the next left subtree
// (this is the "recursive call")
match self.right_nodes.pop() {
Some(node) => self.add_left_subtree(node),
_ => {},
};
return result;
}
}
#[test]
fn test_btree_iteration() {
assert_eq!(new_leaf(123).into_iter().collect::<Vec<_>>(), vec![123]);
let btree = new_branch(new_leaf(10), new_branch(new_leaf(20), new_leaf(30)));
assert_eq!(btree.into_iter().collect::<Vec<_>>(), vec![10, 20, 30]);
let btree = new_branch(
new_branch(
new_branch(new_leaf(9), new_leaf(8)), new_branch(new_leaf(7), new_leaf(6))),
new_branch(
new_branch(new_leaf(5), new_leaf(4)), new_branch(new_leaf(3), new_leaf(2))));
assert_eq!(btree.into_iter().collect::<Vec<_>>(), vec![9, 8, 7, 6, 5, 4, 3, 2]);
}
Output:
$ cargo test
Compiling btree v0.1.0 (file:///path/to/btree)
Running target/debug/btree-76ef84b71534f406
running 2 tests
test test_btree_iteration ... ok
test test_btree_creation ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured