调用Option :: unwrap时,不能移出`& mut`指针的解除引用

时间:2014-12-29 11:21:00

标签: rust

以下代码无法编译:

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);
}

编译器错误是

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

我该如何解决此错误?

1 个答案:

答案 0 :(得分:5)

Rust编译器不会伪装成知识分子或奖学金;它完全按照你告诉的方式,而不是你的意思或想要的。

在这种情况下,当您说&mut root.left.unwrap()时,您想要的内容是Option<Box<Node>>通过可变参考提供的,并为您提供&mut Box<Node>;但这绝对不是你实际告诉它的事情。

你告诉它要做的是对root.left.unwrap()的结果进行可变的引用;让我们看一下Option.unwrap的签名:

fn unwrap(self) -> T

root.left.unwrap()所做的是消耗root.left ,而是生成一个Box<Node>对象(如果它是None则会感到恐慌) 。然后,您将使用整个表达式&mut root.left.unwrap()生成&mut Box<Node>并使用root.left ,从而部分root。现在你绝对不允许这样做,因为它会让root需要被摧毁,但你只有一个可变的引用 - 它不是你的毁灭。因此错误。

解决方案不是使用unwrap而是自己编写一个合适的match表达式。请记住,.unwrap()只是match self { Some(x) => x, None => panic!(…) },没有什么神奇之处。

你最终会得到这样的东西:

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);
    }
}

box ref mut x部分有点纠结,我担心;它会从&mut T产生Box<T>box意味着“取消显示值“和ref mut含义”然后对它进行可变引用“。请记住,所有内容都以模式回到前面,box&与他们在外面的行为相反并且整个批次从左到右而不是从右到左阅读。此外,虽然您可以使用&mut Box<Node>,但我建议使用&mut Node来删除一个间接级别。顺便提一下,如果你 仍在使用&mut Box<Node>root.left.as_mut().unwrap()也可以使用。)

这可以通过不立即将事物填充到root来改进:

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);
    }
}

这样看起来要简单得多,&mut *left从右到左阅读为“取消引用left(这会从Node获得Box<Node>并且然后对它进行可变引用(这会得到&mut Node)。