为何在ReentrantMutexGuard上调用borrow_mut()会触发程序panic?
问题原因分析
你的代码触发panic是因为**RefCell的可变借用规则被违反**,具体流程拆解:
在
main函数中:- 先获取
outer_data.ob的ReentrantMutexGuard(b_mg),由于是可重入锁,后续在foo里再次调用lock不会阻塞。 - 接着通过
b_mg.borrow_mut()拿到InnerData的可变借用(b_data),此时RefCell的可变借用计数为1。
- 先获取
调用
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
相关产品推荐
相关产品推荐

