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

为何临时Ref引发`cannot assign to X`错误,已有Ref却正常?

Rust双向链表遍历的编译错误解析

问题代码

用户编写的遍历双向链表的函数无法编译,代码如下:

use std::cell::RefCell;
use std::rc::{Rc, Weak};

struct Node {
    pub data: i32,
    pub next: Option<Rc<RefCell<Node>>>,
    pub prev: Option<Weak<RefCell<Node>>>,
}

fn print_iterate_doesnt_work(node: &Rc<RefCell<Node>>) {
    let mut current_node = node.clone();
    loop {
        current_node = {
            let current_node_ref = current_node.borrow();
            print!("{} ", current_node_ref.data);
            if current_node_ref.next.is_none() {
                return;
            }

            current_node.borrow().next.as_ref().unwrap().clone()
        }
    }
}

编译报错信息

error[E0506]: cannot assign to `current_node` because it is borrowed
    --> src/lib.rs:13:9
     |
13   |         current_node = {
     |         ^^^^^^^^^^^^ `current_node` is assigned to here but it was already borrowed
...
20   |             current_node.borrow().next.as_ref().unwrap().clone()
     |             ---------------------
     |             |
     |             `current_node` is borrowed here
     |             a temporary with access to the borrow is created here ...
21   |         }
22   |     }
     |     - ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, Node>`
     |
     = note: the temporary is part of an expression at the end of a block;
             consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
     = note: borrow occurs due to deref coercion to `RefCell<Node>`
note: deref defined here
    --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/rc.rs:2124:5
     |
2124 |     type Target = T;
     |     ^^^^^^^^^^^
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
     |
20   |             let x = current_node.borrow().next.as_ref().unwrap().clone(); x
     |             +++++++                                                     +++

For more information about this error, try `rustc --explain E0506`.

错误原因解析

1. 临时Ref的生命周期冲突

Rust有明确的临时变量生命周期规则:块末尾表达式的临时变量会被延长到整个块结束时才销毁。原代码最后一行的current_node.borrow()会创建一个临时的Ref<'_, Node>对象,这个对象要等到块内所有局部变量销毁后,才会执行析构函数释放RefCell的借用权限。

而块开头已经通过current_node_ref = current_node.borrow()获取了一次不可变借用,此时current_node处于被借用状态。当我们试图执行current_node = { ... }赋值操作时,临时Ref还未销毁,current_node的借用状态尚未解除,因此触发了cannot assign to current_node because it is borrowed的错误。

2. 析构函数相关错误信息的含义

Ref类型的析构函数负责将RefCell的借用计数减1,释放借用权限。编译器的担忧在于:临时Ref在块结束时才销毁,此时current_node已经被重新赋值,原有的Rc<RefCell<Node>>可能被丢弃,但临时Ref还持有对旧RefCell的引用,存在悬垂引用的风险——虽然实际运行时不会真的出现问题,但Rust的借用检查器是基于静态规则判断,而非运行时分析。

3. 改用current_node_ref.next...可编译的原因

修改后的代码直接使用已获取的current_node_ref(它是current_node.borrow()得到的Ref引用)访问next,不需要再次调用current_node.borrow()创建新的临时Ref。此时:

  • current_node_ref作为块内局部变量,会在块结束前被销毁,同时释放RefCell的借用权限。
  • 执行current_node = ...赋值时,current_node的借用状态已经解除,完全符合Rust的借用规则。

4. “保存到局部变量”建议的原理

将最后一行的结果存入局部变量x后,current_node.borrow()创建的临时Ref会在赋值给x之后立即销毁(因为x是Rc<RefCell<Node>>的克隆,和Ref无关联)。此时块结束前,临时Ref已经释放了借用权限,current_node就可以被安全赋值。

修复后的代码示例

use std::cell::RefCell;
use std::rc::{Rc, Weak};

struct Node {
    pub data: i32,
    pub next: Option<Rc<RefCell<Node>>>,
    pub prev: Option<Weak<RefCell<Node>>>,
}

fn print_iterate_works(node: &Rc<RefCell<Node>>) {
    let mut current_node = node.clone();
    loop {
        current_node = {
            let current_node_ref = current_node.borrow();
            print!("{} ", current_node_ref.data);
            match &current_node_ref.next {
                None => return,
                Some(next_node) => next_node.clone(),
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Евгений Павлов

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 16:34:53