Skip to main content
edited tags
Link
200_success
  • 144.2k
  • 22
  • 188
  • 473
Source Link
wchargin
  • 1.2k
  • 1
  • 7
  • 20

Basic binary tree manipulation in Rust

The goal here is to implement a basic binary tree with values only on leaves, with depth, mirror, and in_order (traversal) operations.

I have not used Rust before.

Some particular questions I have:

  • In a few places, I'm defining methods by passing self by reference and then matching on its derefenced form. I then have to borrow the fields with ref in the destructuring. Is this the intended form?
  • Is into_in_order using Vec properly/optimally? (I tried to use just lv.extend(&mut rv) but got an error that that particular method was still under churn and I should wait for "the dust to settle.")
  • Am I using Boxes correctly? When should I use Boxes vs. * consts?
  • Why do (*r).mirror() and r.mirror() do the same thing? I would have expected the latter to throw because Boxes don't have a mirror method.
  • Is there a less verbose alternative to the Branch(Box::new(Branch(…))) syntax?
#[derive(Debug, PartialEq, Clone)]
enum BTree<T> {
    Leaf(T),
    Branch(Box<BTree<T>>, Box<BTree<T>>),
}

impl <T> BTree<T> {

    fn depth(&self) -> i32 {
        match *self {
            BTree::Leaf(_) => 1,
            BTree::Branch(ref l, ref r) =>
                std::cmp::max(l.depth(), r.depth()) + 1,
        }
    }

    fn into_in_order(self) -> Vec<T> {
        match self {
            BTree::Leaf(val) => vec!(val),
            BTree::Branch(l, r) => {
                let mut lv = l.into_in_order();
                let rv = r.into_in_order();
                lv.extend(rv.into_iter());
                lv
            }
        }
    }

}

impl <T : Clone> BTree<T> {

    fn mirror(&self) -> BTree<T> {
        match *self {
            BTree::Leaf(_) => (*self).clone(),
            BTree::Branch(ref l, ref r) =>
                BTree::Branch(Box::new((*r).mirror()), Box::new((*l).mirror())),
                //
                // why does this work?
                // BTree::Branch(Box::new(r.mirror()), Box::new(l.mirror())),
        }
    }

}

#[test]
#[allow(unused_variables)]
fn test_btree_creation() {
    use BTree::*;

    let leaf: BTree<i32> = Leaf(10);
    let branch: BTree<i32> = Branch(Box::new(Leaf(15)), Box::new(Leaf(20)));
    let tree: BTree<i32> = Branch(Box::new(branch.clone()), Box::new(Leaf(30)));

    assert_eq!(branch, branch.clone());
}

#[test]
fn test_btree_depth() {
    use BTree::*;

    assert_eq!(Leaf(10).depth(), 1);

    let branch: BTree<i32> = Branch(Box::new(Leaf(15)), Box::new(Leaf(20)));
    assert_eq!(branch.depth(), 2);

    let tree: BTree<i32> = Branch(Box::new(branch.clone()), Box::new(Leaf(30)));
    assert_eq!(tree.depth(), 3);

    let other_tree: BTree<i32> = Branch(
        Box::new(branch.clone()), Box::new(branch.clone()));
    assert_eq!(other_tree.depth(), 3);
}

#[test]
fn test_btree_mirror() {
    use BTree::*;

    assert_eq!(Leaf(10).mirror(), Leaf(10));

    assert_eq!(
        Branch(Box::new(Leaf(10)), Box::new(Leaf(20))).mirror(),
        Branch(Box::new(Leaf(20)), Box::new(Leaf(10))));

    assert_eq!(
        Branch(
            Box::new(Leaf(10)),
            Box::new(Branch(Box::new(Leaf(20)), Box::new(Leaf(30))))
        ).mirror(),
        Branch(
            Box::new(Branch(Box::new(Leaf(30)),
            Box::new(Leaf(20)))), Box::new(Leaf(10))));
}

#[test]
fn test_btree_in_order() {
    use BTree::*;

    assert_eq!(Leaf(10).into_in_order(), vec!(10));

    assert_eq!(Branch(Box::new(Leaf(10)), Box::new(Leaf(20))).into_in_order(),
        vec!(10, 20));

    assert_eq!(
        Branch(
            Box::new(Leaf(10)),
            Box::new(Branch(Box::new(Leaf(20)), Box::new(Leaf(30))))
        ).into_in_order(),
        vec!(10, 20, 30));
}

I also have the following macro definitions (not all used above):

macro_rules! assert_eq {
    ($actual:expr, $expected:expr) => (
        if $expected != $actual {
            panic!("expected {:?}, but got {:?}", $expected, $actual);
        }
    )
}
macro_rules! assert_neq {
    ($actual: expr, $expected_not: expr) => (
        if $expected_not == $actual {
            panic!("expected {:?} not to equal {:?}", $expected_not, $actual);
        }
    )
}
macro_rules! assert_approx {
    ($actual: expr, $expected: expr) => {
        if ($expected - $actual).abs() > 1e-3 {
            panic!("expected {:?} or similar, but got {:?}", $expected, $actual);
        }
    }
}