为什么在 `while let Some() = xxx` 循环之后仍然借用 `tail` ?

Sve*_*rev 4 rust

我一直在解决一些 leetcode 问题,但遇到了一个我无法解释的问题。

这是完整的源代码:

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

impl ListNode {
    fn new(val: i32) -> Self {
        ListNode { next: None, val }
    }
}

fn main() {
    let mut list = Some(Box::new(ListNode {
        val: 1,
        next: Some(Box::new(ListNode {
            val: 2,
            next: Some(Box::new(ListNode { val: 3, next: None })),
        })),
    }));

    let mut tail = &mut list.as_mut().unwrap().next;

    // This compiles fine
    // while tail.is_some() {
    //     tail = &mut tail.as_mut().unwrap().next;
    // }

    // This compiles fine
    // while let Some(ref mut node) = tail {
    //     tail = &mut node.next;
    // }

    // However this does not
    while let Some(node) = tail.as_mut() {
        tail = &mut node.next;
    }

    *tail = Some(Box::new(ListNode::new(4)));
    println!("{:#?}", list);
}
Run Code Online (Sandbox Code Playgroud)

这是错误:

error[E0506]: cannot assign to `*tail` because it is borrowed
  --> src/main.rs:39:5
   |
35 |     while let Some(node) = tail.as_mut() {
   |                            ---- borrow of `*tail` occurs here
...
39 |     *tail = Some(Box::new(ListNode::new(4)));
   |     ^^^^^
   |     |
   |     assignment to borrowed `*tail` occurs here
   |     borrow later used here

Run Code Online (Sandbox Code Playgroud)

为什么循环tail仍然借用?while let Some() = xxx

use*_*968 5

这是由于thatwhile let ...和中的审查者 的交互作用,它是地点表达式上下文中的值表达式。tail.as_mut()while let

简而言之,为了让循环体while let能够访问绑定 ( node),编译器需要借用tail(在循环之前创建)、调用as_mut()Option保留隐式临时借用Option(!),以及绑定值as_mut()返回到node;据编译器所知, 的生命周期node取决于Option,因为它是通过 漏斗的&'a mut tail -> as_mut(&'a mut self) -> &'a mut ListNode

当您重新分配时tail = &mut node.next,这仍然是隐式生命周期的值'a,借用循环创建的原始临时值。

*tail当您在循环后分配给时,会发生冲突:原始 上有一个隐式借用Option,它用于调用as_mut()首先创建node然后然后分配tail,但现在您正在分配给 - 就编译器而言 -选项Option但是在借用时您无法使其无效,因此会发生错误。

这就是为什么编译器指向borrow of *tail occurs here(注意取消引用)循环的审查位置while let,然后 - 令人困惑 - 指向*tail = Some(...)使用借用的点。

另外两个示例编译良好,因为在 scrutinee 中没有创建临时对象来执行方法调用Option