为什么Rust元组模式匹配需要显式借用?

时间:2018-05-10 03:42:27

标签: rust pattern-matching move-semantics

我正在Rust写一个二叉树,借用检查器真让我困惑。这是a minimal example that reproduces the problem

二叉树的定义如下:

struct NonEmptyNode;

pub struct BinaryTree {
    root: Option<NonEmptyNode>,
}

借用检查程序拒绝的代码是:

// Implementation #1
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
    match (self.root, bt.root) {
        (Some(ref rt), Some(ref node)) => {
            setter(rt, node);
            true
        }
        _ => false,
    }
}

错误消息是

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:10:16
   |
10 |         match (self.root, bt.root) {
   |                ^^^^ cannot move out of borrowed content

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:10:27
   |
10 |         match (self.root, bt.root) {
   |                           ^^ cannot move out of borrowed content

要使其正常工作,必须将代码修改为:

// Implementation #2
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
    match (&self.root, &bt.root) {
        // explicit borrow
        (&Some(ref rt), &Some(ref node)) => {
            // explicit borrow
            setter(rt, node);
            true
        }
        _ => false,
    }
}

如果我一次模拟匹配一个变量而没有明确借用,借用检查员根本不会抱怨:

// Implementation #3
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
    match self.root {
        Some(ref rt) => match bt.root {
            // No explict borrow will be fine
            Some(ref node) => {
                // No explicit borrow will be fine
                setter(rt, node);
                true
            }
            _ => false,
        },
        _ => false,
    }
}

为什么实现#3不需要显式借用,而实现#1呢?

1 个答案:

答案 0 :(得分:3)

关键是self.rootbt.root"place expression" s,而元组则不是。 #3的工作原理是编译器知道如何通过&#34;中间表达式,以便绑定到原始存储位置。

你可以看到它的另一种方式:像self.root这样非常简单的表达式是特殊的,它们的外观和行为类似于值(并且具有值类型),但是秘密地编译器会记住它是如何达到该值的,允许它返回并获取指向该值的读取位置。

如果有什么东西可以解决问题的简单方法&#34;放置表达式&#34;是尝试为它赋值。如果您可以expr = some_value;,则expr必须是&#34;地点表达式&#34;。顺便说一下,这也是为什么当您编写&self.root时,您会获得指向存储self.root的位置的指针,而不是指向self.root副本的指针。

这个&#34;地方表达&#34;由于他们没有这个属性,因此他们不会为元组工作。要构造元组,编译器必须实际读取元组元素的值,并将它们移动或复制到元组的新存储中。这会破坏编译器可能具有的任何位置关联:这些值字面上不再是以前的位置。

最后,您可能希望查看Option::as_ref,将&Option<T>变为Option<&T>。这样您就可以在(self.root.as_ref(), bt.root.as_ref())上匹配(Some(rt), Some(node))这样的模式,这可能会更方便。