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

为何在ReentrantMutexGuard上调用borrow_mut()会触发程序panic?

问题原因分析

你的代码触发panic是因为**RefCell的可变借用规则被违反**,具体流程拆解:

  1. 在main函数中:

    • 先获取outer_data.ob的ReentrantMutexGuard(b_mg),由于是可重入锁,后续在foo里再次调用lock不会阻塞。
    • 接着通过b_mg.borrow_mut()拿到InnerData的可变借用(b_data),此时RefCell的可变借用计数为1。
  2. 调用outer_data.foo()时:

    • foo里再次获取ob的锁(可重入特性允许同一线程重复获取,所以能成功拿到)。
    • 尝试再次调用borrow_mut()获取InnerData的可变借用,但此时RefCell已经存在一个未释放的可变借用(来自main里的b_data)——而RefCell的核心规则是同一时间只能存在一个可变借用,因此直接触发panic。
修复方案

你需要确保在调用foo前释放main里的RefCell可变借用,或者调整逻辑避免嵌套的可变借用:

方案1:提前释放借用

利用Rust的作用域特性,将main里的锁和借用放在单独代码块中,离开代码块后自动释放:

use std::sync::Mutex;
use std::cell::RefCell;

use parking_lot::ReentrantMutex;

#[derive(Default)]
struct InnerData {
    ia: i32,
    ib: i32,
}

#[derive(Default)]
struct OuterData {
    oa: i32,
    ob: ReentrantMutex<RefCell<InnerData>>,
}

impl OuterData {
    fn foo(&self) {
        println!("fn foo begin");
        let mut b_mg = self.ob.lock();
        let mut b_data = b_mg.borrow_mut();
        println!("after acquiring lock");
        if b_data.ia == 3 {
            b_data.ib = 5;
        }
        println!("fn foo end");
    }
}

fn main() {
    let mut outer_data: OuterData = Default::default();
    {
        let mut b_mg = outer_data.ob.lock();
        let mut b_data = b_mg.borrow_mut();
        b_data.ia = 3;
        // 离开此代码块,b_data和b_mg自动被丢弃,释放所有借用
    }

    let mut b_mg = outer_data.ob.lock();
    let b_data = b_mg.borrow();
    if b_data.ia == 3 {
        outer_data.foo();
    }
}

方案2:调整逻辑避免嵌套借用

如果需要保留main里的锁,可以将需要检查的状态提前取出,传递给foo,避免重复获取可变借用:

use std::sync::Mutex;
use std::cell::RefCell;

use parking_lot::ReentrantMutex;

#[derive(Default)]
struct InnerData {
    ia: i32,
    ib: i32,
}

#[derive(Default)]
struct OuterData {
    oa: i32,
    ob: ReentrantMutex<RefCell<InnerData>>,
}

impl OuterData {
    fn foo(&self, target_ia: i32) {
        println!("fn foo begin");
        let mut b_mg = self.ob.lock();
        if target_ia == 3 {
            let mut b_data = b_mg.borrow_mut();
            b_data.ib = 5;
        }
        println!("fn foo end");
    }
}

fn main() {
    let mut outer_data: OuterData = Default::default();
    let mut b_mg = outer_data.ob.lock();
    let mut b_data = b_mg.borrow_mut();
    b_data.ia = 3;
    let current_ia = b_data.ia;

    if current_ia == 3 {
        outer_data.foo(current_ia);
    }
}
关键知识点
  • ReentrantMutex仅解决互斥锁的重入问题,允许同一线程多次获取锁,但它不会改变内部RefCell的借用规则。
  • RefCell是运行时检查借用约束的,其规则和编译期的&mut引用完全一致:同一时间只能存在一个可变借用,或多个不可变借用,违反则直接panic。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 09:40:26