为什么匹配的可变引用的条件赋值会导致借用错误?

Why does conditional assignment of a matched mutable reference cause borrow errors?

我无法理解为什么这会导致错误:

#[derive(Debug)]
pub struct Node {
    next: Option<Box<Node>>,
}

pub fn print_root_or_next(root: &mut Node, try_next: bool) {
    let mut current = root;
    match &mut current.next {
        Some(node) => {
            if try_next {
                current = &mut *node;
            }
        }
        None => return,
    }

    println!("{:?}", current);
}
error[E0502]: cannot borrow `current` as immutable because it is also borrowed as mutable
  --> src/lib.rs:17:22
   |
8  |     match &mut current.next {
   |           ----------------- mutable borrow occurs here
...
17 |     println!("{:?}", current);
   |                      ^^^^^^^
   |                      |
   |                      immutable borrow occurs here
   |                      mutable borrow later used here

我看不出有冲突的借用;甚至错误消息似乎也表明两次借用是一回事。这是借用检查器的问题还是这个例子在某些方面确实存在缺陷?

我对这个限制很感兴趣。我对实现不太了解,但考虑到基本的 if:

if try_next {
    current = &mut *current.next.as_mut().unwrap();
}

和基本 match:

match &mut current.next {
    Some(node) => {
        current = &mut *node;
    }
    None => return,
}

甚至反转它们:

if try_next {
    match &mut current.next {
        Some(node) => {
            current = &mut *node;
        }
        None => return,
    }
}

一切正常,一定有一些事情是借阅检查员正在考虑或没有考虑的,这与我对原始表格为何不起作用的理解相冲突。

我认为问题在于 current 本身就是有条件的再借。

考虑删除条件但使用两个变量的更简单的代码:

#[derive(Debug)]
pub struct Node {
    next: Option<Box<Node>>,
}

pub fn print_root_or_next(root: &mut Node) {
    let a = root;
    let b;
    match &mut a.next {
        Some(node) => {
            b = &mut *node;
        }
        None => return,
    }

    println!("{:?}", a);
    println!("{:?}", b);
}

这失败了,正如您所期望的错误消息:

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
  --> src/lib.rs:16:22
   |
9  |     match &mut a.next {
   |           ----------- mutable borrow occurs here
...
16 |     println!("{:?}", a);
   |                      ^ immutable borrow occurs here
17 |     println!("{:?}", b);
   |                      - mutable borrow later used here

也就是说,a 是可变借用的,并且由于 b 而使借用保持有效。所以当 b 还活着时你不能使用 a

如果你重新排序最后两行 println!,它编译没有问题,因为词法生命周期:b 被打印然后被遗忘,释放借用并使 a再次可用。

现在,看看另一个变体,与您的变体相似,但没有 if:

pub fn print_root_or_next(root: &mut Node) {
    let mut c = root;
    match &mut c.next {
        Some(node) => {
            c = &mut *node;
        }
        None => return,
    }
    println!("{:?}", c);
}

它编译也很好,因为当 c 被重新借用时,它被重新分配。从这一点开始,它就像前面示例中的 b 一样工作。你可以自由使用b,禁止使用a,这里不再提供。

返回您的代码:

pub fn print_root_or_next(root: &mut Node, test: bool) {
    let mut c = root;
    match &mut c.next {
        Some(node) => {
            if test {
                c = &mut *node;
            }
        }
        None => return,
    }
    println!("{:?}", c);
}

这里的问题是,当c被重新借用时,它是有条件地完成的,编译器不知道它会是哪一个,像a或像b来自上面的例子。所以它必须假设它是同时存在的!但是从示例中我们看到,当 b 处于活动状态时,您不能使用 a,但由于它们都是相同的值,因此不能再使用该值。

当编译器抱怨这个奇怪的错误消息时:

17 | , current);
   | ^^^^^^^
   | |
   | immutable borrow occurs here
   | mutable borrow later used here

实际意思是:

17 | , current);
   | ^^^^^^^
   | |
   | immutable borrow occurs here if try_next is false
   | mutable borrow at the same time used here if try_next is true

我不知道这是否是编译器的限制,使用这个有条件地重新借用自身的引用实际上是安全的。也许这是对借用检查的限制,或者也许有一些我不明白的微妙之处......我怀疑如果你包括多个条件或多个参考,允许这可能是不合理的。