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

Rust 为什么匹配可变引用的条件赋值会导致借用错误?,rust,borrow-checker,Rust,Borrow Checker,我无法理解这会导致错误的原因: #[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) => {

我无法理解这会导致错误的原因:

#[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);
}
我看不出有多少相互冲突的借款;甚至错误消息似乎也表明两个借词是一个且相同的。这是借用检查器的一个问题,还是这个示例在某些方面确实存在缺陷

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

if try_next {
    current = &mut *current.next.as_mut().unwrap();
}
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);
}
与基本
匹配

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);
}
也就是说,
a
是可变借用的,而该借用由于
b
而保持活动状态。因此,当
b
处于活动状态时,不能使用
a

如果您重新排序最后两个
println行,它编译时没有问题,因为词法寿命:
b
被打印,然后被遗忘,释放借阅并使
a
再次可用

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

if try_next {
    current = &mut *current.next.as_mut().unwrap();
}
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

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

借款是如何一个一个的?您正在使用
&mut current.next
可变地借用
current
,然后使用
println不可变地借用它奇怪,删除if检查(
if try\u next
),如果不重新初始化当前的
代码,则@IbraheemAhmed对该代码的注释的代码将编译——这可能是因为如果只分配一次变量,编译器可以在编译时跟踪它,因此它能够将可变借用的生存期缩短到
match
语句的末尾,因为这是最后一次需要它是可变的。另一方面,如果您重新分配变量,编译器可能甚至不会尝试缩短生存期,因为它依赖于运行时,并且它不会编译,因为在执行
println
时,您可能会有
root
的可变借用;TL;DR the duplicate:借阅检查器不知道借阅未保存在
None
案例中。感谢链接@Shepmaster!虽然我仍然困惑;大多数解释都是围绕着返回的参考文献,而这些参考文献并不是这样的。我最初以为你说的是
返回
以某种方式扩展了借用,但是如果
产生
()
而不是返回,则是相同的错误。如果它“不知道借款不在
None
案例中”,为什么它与不包括if的基本匹配没有问题?对于记录,原始代码与
-Zpolonius
编译良好,因此我怀疑允许这样做是合理的。它类似于最初应该由NLL修复的“”,但不是出于编译器性能的原因。