3

I am fairly new to Rust and want to implement an AVL-Tree.

I am using the following enum to represent my tree:

enum AvlTree<T> {
    Leaf,
    Node {
        left: Box<AvlTree<T>>,
        right: Box<AvlTree<T>>,
        value: T
    }
}

When implementing one of the balance-functions, I'm facing some problems with ownership and borrowing.

I am trying to write a function, which takes an AvlTree<T> and returns another AvlTree<T>. My first attempt was something like this:

fn balance_ll(tree: AvlTree<T>) -> AvlTree<T> {
    if let AvlTree::Node {left: t, right: u, value: v} = tree {
        if let AvlTree::Node {left: ref tl, right: ref ul, value: ref vl} = *t {
            AvlTree::Leaf // Return a new AvlTree here
        } else {
            tree
        }
    } else {
        tree
    }
}

Even with this minimal example, the compiler is returning an error:

error[E0382]: use of partially moved value: `tree`             
  --> avl.rs:67:17             
   |                           
63 |         if let AvlTree::Node {left: t, right: u, value: v} = tree {                                                       
   |                                     - value moved here    
...                            
67 |                 tree      
   |                 ^^^^ value used here after move           
   |                           
   = note: move occurs because `(tree:AvlTree::Node).left` has type `std::boxed::Box<AvlTree<T>>`, which does not implement the `Copy` trait                  

I think, I'm understanding the error message correctly, in that destructuring the AvlTree::Node will take away the ownership of the tree example. How can I prevent this from happening? I already tried various things and (de-)referencing the tree-variable only to face more errors.

Additionally, I want to use some of the extracted values like u, tl and vl in the new struct. Is this possible and could you maybe provide a minimal example doing exactly that? I don't need access to the old tree after executing the function.

lschuermann
  • 862
  • 1
  • 7
  • 17

1 Answers1

3

I think, I'm understanding the error message correctly, in that destructuring the AvlTree::Node will take away the ownership of the tree example.

Yes. If you still need to be able to use tree afterwards, you will either need to make a copy of it:

#[derive(Clone)]
enum AvlTree<T> {...}

fn balance_ll<T: Clone>(tree: AvlTree<T>) -> AvlTree<T> {
    let copy = tree.clone();

    if let AvlTree::Node { left: t, right: u, value: v } = tree {
        if let AvlTree::Node { left: ref tl, right: ref ul, value: ref vl } = *t {
            AvlTree::Leaf // Return a new AvlTree here
        } else {
            copy
        }
    } else {
        tree
    }
}

or use a helper function that quickly releases its ownership - but I don't think it is possible without box patterns:

#![feature(box_patterns)]

impl<T> AvlTree<T> {
    fn is_left_node(&self) -> bool {
        if let &AvlTree::Node { left: ref t, right: ref u, value: ref v } = self {
            if let &box AvlTree::Node { left: ref tl, right: ref ul, value: ref vl } = t {
                true
            } else {
                false
            }
        } else {
            false
        }
    }
}

fn balance_ll<T>(tree: AvlTree<T>) -> AvlTree<T> {
    if tree.is_left_node() {
        AvlTree::Leaf
    } else {
        tree
    }
}

Since you possibly want to use the destructured values you will probably prefer the clone variant, but maybe the other one will give you some extra ideas.

ljedrz
  • 20,316
  • 4
  • 69
  • 97