1
votes

The following code doesn't compile:

struct Node {
    left: Option<Box<Node>>,
    right: Option<Box<Node>>,
}

impl Node {
    fn new() -> Node {
        Node { left: None, right: None, }
    }
}

fn init_tree(root: &mut Box<Node>, n: int) {
    match n {
        0 => {},
        _ => {
            root.left   = Some(box Node::new());
            root.right  = Some(box Node::new());
            init_tree(&mut root.left.unwrap(), n - 1);
            init_tree(&mut root.left.unwrap(), n - 1);
        }
    }
}

fn main() {
    let mut root: Box<Node> = box Node::new();
    init_tree(&mut root, 10);
}

The compiler error is

error: cannot move out of dereference of `&mut`-pointer
       init_tree(&mut root.left.unwrap(), n - 1);
                      ^~~~

How could I fix this error?

1
FYI, your code initializes left twice; I hope that's not surprising to you! ^_^Shepmaster

1 Answers

5
votes

The Rust compiler makes no pretence to intellectual eminence or scholarship sublime; it does exactly what you tell it to, not what you meant or wanted.

In this case, when you say &mut root.left.unwrap(), what you are wanting is something that takes Option<Box<Node>> by mutable reference and gives you a &mut Box<Node>; but that’s definitely not what you actually told it to do.

What you told it to do was to take a mutable reference to the result of root.left.unwrap(); let’s look at the signature for Option.unwrap:

fn unwrap(self) -> T

So what root.left.unwrap() does is it consumes root.left, producing in its stead a Box<Node> object (or panicking if it was None). You are then left with the entire expression &mut root.left.unwrap() producing &mut Box<Node>, and consuming root.left and hence partially root. Now this you are definitely not permitted to do, as it would leave root needing to be destroyed, but you only had a mutable reference to it—it is not yours to destroy. Hence the error.

The solution is not to use unwrap but rather to write a suitable match expression yourself. Remember, .unwrap() is just match self { Some(x) => x, None => panic!(…) }, there’s nothing magic about it.

You’ll end up with something like this:

fn init_tree(root: &mut Node, n: int) {
    if n != 0 {
        root.left = Some(box Node::new());
        root.right = Some(box Node::new());
        init_tree(match root.left { Some(box ref mut x) => x, None => unreachable!() }, n - 1);
        init_tree(match root.right { Some(box ref mut x) => x, None => unreachable!() }, n - 1);
    }
}

(The box ref mut x part is a bit of a tangle, I’m afraid; it produces a &mut T from a Box<T>, the box meaning “unbox the value” and the ref mut meaning “and then take a mutable reference to it”. Remember that everything is back to front in patterns, with box and & meaning the opposite to what they do outside and with the whole lot reading left to right rather than right to left. Also, while you could work with &mut Box<Node> I would recommend using &mut Node there which removes one level of indirection. Incidentally if you were still using &mut Box<Node>, root.left.as_mut().unwrap() would work as well.)

This can be improved by not stuffing the thing into root immediately:

fn init_tree(root: &mut Node, n: int) {
    if n != 0 {
        let mut left = box Node::new();
        let mut right = box Node::new();
        init_tree(&mut *left, n - 1);
        init_tree(&mut *right, n - 1);
        root.left = Some(left);
        root.right = Some(right);
    }
}

That is a lot simpler to look at, &mut *left being read right to left as “dereference left (which will get you a Node from a Box<Node>) and then take a mutable reference to it (which will get you a &mut Node).