You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust单链表删除指定值代码编译报错问题求助

Rust单链表删除节点的借用编译错误解析

问题描述

编写单链表删除指定值节点的代码时,遇到了借用检查器的编译错误。当使用head = &mut node.next;替代head = &mut head.as_mut().unwrap().next;时,在*head = node.next.take();处报错:

cannot assign to *head because it is borrowed *head is assigned to here but it was already borrowed

疑惑为什么两种写法逻辑看似等价,却触发不同的编译结果。

问题代码

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

impl ListNode {
  #[inline]
  fn new(val: i32) -> Self {
    ListNode {
      next: None,
      val
    }
  }
}
pub fn delete_node(head: Option<Box<ListNode>>, val: i32) -> Option<Box<ListNode>> {
    let mut res =  head;
    let mut head = & mut res;
    while let Some(node) = head {
        if node.val != val {
            // 此写法正常编译
            head = &mut head.as_mut().unwrap().next;
            // 此写法会触发编译错误
            // head = &mut node.next;
        } else {
            *head = node.next.take();
            break;
        }
    }
    res
}

错误原因解析

核心问题在于Rust借用检查器的可变引用排他性规则:同一时间,一个值只能存在一个可变引用,且引用存续期间不能修改被引用的值。

两种写法的本质差异

  1. head = &mut node.next;的问题
    当while let Some(node) = head执行时,node是对head当前指向的Box<ListNode>的可变引用,这个引用的生命周期会覆盖整个循环块。
    当你执行head = &mut node.next;,head现在指向node内部的next字段,但node本身仍然持有对原head值的可变引用。此时:

    • 原head值被node借用着
    • head新指向的node.next是node引用对象的一部分
      当进入else分支执行*head = node.next.take();时,你尝试修改head当前指向的node.next,但node的可变引用还未释放——这相当于同时存在两个对node.next的可变引用(node本身可以访问node.next,head也持有它的可变引用),直接违反了可变引用的排他性规则,因此触发编译错误。
  2. head = &mut head.as_mut().unwrap().next;为什么正常
    这个写法是直接从当前的head引用中重新获取可变访问权:

    • head.as_mut()获取当前head指向的Option的可变引用
    • unwrap()得到内部Box<ListNode>的可变引用
    • 最后取next的可变引用赋值给head
      这里关键在于,原node引用在这一行之后就不再被使用,借用检查器可以推断出node的生命周期已经结束,原head值的借用权被释放。后续进入else分支修改*head时,不存在任何未释放的可变引用冲突,因此可以正常编译。

修正后的可行写法

如果想使用head = &mut node.next;的写法,可以调整循环逻辑,确保node的引用在赋值前被正确释放:

pub fn delete_node(head: Option<Box<ListNode>>, val: i32) -> Option<Box<ListNode>> {
    let mut res = head;
    let mut head = &mut res;
    
    loop {
        match head {
            None => break,
            Some(node) if node.val == val => {
                *head = node.next.take();
                break;
            }
            Some(node) => {
                // 此处node的引用仅用于获取next,之后不再被使用
                head = &mut node.next;
            }
        }
    }
    
    res
}

这种写法中,每个match分支执行完毕后,对应的node引用就会被丢弃,不会存在跨分支的借用冲突,因此可以正常通过编译。

内容的提问来源于stack exchange,提问作者RainChestnut

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 18:24:50