To teach myself Rust, I implemented a naive binary tree with support for insert, delete and lookup operations as well as in-order iteration.
I'm still a little rusty (no pun intended) when it comes to writing idiomatic code and especially elegant handling of borrows.
use std::cmp::Ordering;
use std::cmp::Ordering::{Less, Greater, Equal};
use std::mem::swap;
// A type implementing Indexed<K> that is used as value in a BinaryTree may be indexed by K,
// that is, lookup functions can take a key: K instead of the full value. This is necessary for
// implementing associative containers.
pub trait Indexed<K: ?Sized> {
fn key(&self) -> &K;
}
impl<T> Indexed<T> for T where T: Ord {
fn key(&self) -> &T { self }
}
type Link<T> = Option<Box<Node<T>>>;
struct Node<T> {
value: T,
left: Link<T>,
right: Link<T>
}
pub struct BinaryTree<T> {
root: Link<T>
}
impl<T> BinaryTree<T> {
pub fn new() -> Self {
BinaryTree { root: None }
}
// Get a reference to the link at which "key" is or should be located
fn locate<K>(&self, key: &K) -> &Link<T> where T: Indexed<K>, K: Ord {
let mut anchor = &self.root;
while let Some(ref node) = *anchor {
match node.value.key().cmp(&key) {
Less => anchor = &node.left,
Greater => anchor = &node.right,
Equal => return anchor
}
}
// No such entry, anchor is pointing to the insert position of value
anchor
}
// Like locate(), but returns a &mut for insertion and deletion
fn locate_mut<K>(&mut self, key: &K) -> &mut Link<T> where T: Indexed<K>, K: Ord {
let mut anchor = &mut self.root;
loop {
// Not as simple as locate(): The binding of anchor must be removed before
// destructuring and re-assigning it in order to avoid duplicate &muts
match {anchor} {
&mut Some(ref mut node) if key != node.value.key() => {
anchor = if key < node.value.key() { &mut node.left } else { &mut node.right }
},
// Either &mut Some(node) with node.value == value or &mut None if value was
// not found
other => return other
}
}
}
pub fn lookup<K>(&self, key: &K) -> Option<&T> where T: Indexed<K>, K: Ord {
self.locate(key).as_ref().map(|node| &node.value)
}
pub fn insert(&mut self, value: T) -> bool where T: Ord {
let anchor = self.locate_mut(&value);
match *anchor {
Some(_) => false,
None => {
*anchor = Some(Box::new(Node { value: value, left: None, right: None }));
true
}
}
}
pub fn delete<K>(&mut self, key: &K) where T: Indexed<K>, K: Ord {
delete_node(self.locate_mut(key));
}
pub fn iter(&self) -> Iter<T> {
Iter { current: &self.root, stack: Vec::new() }
}
}
// Returns the next in-order successor in a subtree
fn successor<T>(mut next: &mut Link<T>) -> &mut Link<T> {
loop {
match {next} {
&mut Some(ref mut node) if node.left.is_some() => next = &mut node.left,
other => {
debug_assert!(other.is_some());
return other;
}
}
}
}
// Removes a node, either by simply discarding it if it is a leaf, or by swapping it with
// its inorder successor (which, in this case, is always in a leaf) and then deleting the leaf.
fn delete_node<T>(link: &mut Link<T>) {
if let Some(mut boxed_node) = link.take() {
match (boxed_node.left.take(), boxed_node.right.take()) {
(None, None) => (),
(Some(left), None) => *link = Some(left),
(None, Some(right)) => *link = Some(right),
(Some(left), Some(right)) => {
// take() followed by re-assignment looks like an awful hackjob, but appears
// to be the only way to satisfy all cases in the match
{
let node = &mut *boxed_node; // unbox
node.left = Some(left);
node.right = Some(right);
let next = successor(&mut node.right);
swap(&mut node.value, &mut next.as_mut().unwrap().value);
delete_node(next);
}
*link = Some(boxed_node);
}
}
}
}
// Allow iterating over &tree
impl<'a, T: 'a> IntoIterator for &'a BinaryTree<T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct Iter<'a, T: 'a> {
current: &'a Link<T>,
stack: Vec<&'a Node<T>>
}
impl<'a, T: 'a> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
while let Some(ref node) = *self.current {
self.stack.push(node);
self.current = &node.left;
}
self.stack.pop().map(|node| {
self.current = &node.right;
&node.value
})
}
}
An example using the above code to represent a set:
fn main() {
let mut set = BinaryTree::new();
for value in &[100, 5, 1, 10, -1] {
set.insert(*value);
}
for value in &[5, 1, 151] {
set.delete(value);
}
for value in &set {
println!("{}", value);
}
}
And a map with a custom Entry
type:
#[derive(Debug, Copy, Clone)]
pub struct Entry<K, V>(K, V);
// Make Entry<K, V> indexable and ordered by K
impl<K, V> Indexed<K> for Entry<K, V> where K: Ord {
fn key(&self) -> &K {
&self.0
}
}
impl<K, V> PartialEq for Entry<K, V> where K: PartialEq {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl<K, V> Eq for Entry<K, V> where K: Eq {
}
impl<K, V> PartialOrd for Entry<K, V> where K: PartialOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<K, V> Ord for Entry<K, V> where K: Ord {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
// Example
fn main() {
let mut map = BinaryTree::new();
for entry in &[Entry("France", "Paris"), Entry("Germany", "Berlin"), Entry("Italy", "Rome")] {
map.insert(*entry);
}
for &Entry(key, value) in &map {
println!("{} => {}", key, value);
}
}
{anchor}
instead ofanchor
infn locate_mut
\$\endgroup\$anchor
is re-assigned, so it must be explicitly moved before reassignment by writing{anchor}
. Otherwise the borrow checker will complain about an aliased&mut
. \$\endgroup\$anchor= next_anchor; match anchor { ... next_anchor = x ... }
is it more explicit? \$\endgroup\$