Reputation: 107
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?
Upvotes: 1
Views: 1087
Reputation: 90742
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
).
Upvotes: 6