如何避免Rust中的多次可变借用问题?
问题代码
use std::collections::HashMap; struct A { a: i32 } impl A { fn fun1(&mut self, i: i32) { println!("fun1: {}, {}", i, self.a); } fn fun2(&mut self, i: i32) { println!("fun2: {}, {}", i, self.a); } } fn main() { let mut a_obj = A { a: 9 }; let mut l: HashMap<String, Box<dyn FnMut(i32)>> = HashMap::new(); l.insert(String::from("abc"), Box::new(|param| a_obj.fun1(param))); l.insert(String::from("def"), Box::new(|param| a_obj.fun2(param))); let key1 = String::from("abc"); let h1 = l.get_mut(&key1).unwrap(); h1(3); let key2 = String::from("def"); let h2 = l.get_mut(&key2).unwrap(); h2(686); }
报错信息
error[E0499]: cannot borrow
a_objas mutable more than once at a time
--> src/main.rs:24:44
|
23 | l.insert(String::from("abc"), Box::new(|param| a_obj.fun1(param)));
| ------- ----- first borrow occurs due to use ofa_objin closure
| |
| first mutable borrow occurs here
24 | l.insert(String::from("def"), Box::new(|param| a_obj.fun2(param)));
| ^^^^^^^ ----- second borrow occurs due to use ofa_objin closure
| |
| second mutable borrow occurs here
...
27 | let h1 = l.get_mut(&key1).unwrap();
| ---------------- first borrow later used hereFor more information about this error, try
rustc --explain E0499.
问题原因
Rust的借用检查器严格遵循「同一时间只能存在一个可变引用」的规则。这里两个闭包在创建时都捕获了a_obj的可变引用,相当于同时持有两个活跃的可变借用,直接触发编译错误。加上闭包被存储在HashMap中,生命周期被延长,借用状态会持续到闭包被调用,进一步加剧了冲突。
解决方案:使用内部可变性(RefCell)
通过RefCell实现内部可变性,允许在共享引用的基础上,运行时动态获取可变引用,绕开编译期的借用检查(运行时会验证借用规则,违反时触发panic)。
修改后的代码:
use std::collections::HashMap; use std::cell::RefCell; struct A { a: i32 } impl A { fn fun1(&mut self, i: i32) { println!("fun1: {}, {}", i, self.a); } fn fun2(&mut self, i: i32) { println!("fun2: {}, {}", i, self.a); } } fn main() { let a_obj = RefCell::new(A { a: 9 }); let mut l: HashMap<String, Box<dyn FnMut(i32)>> = HashMap::new(); l.insert(String::from("abc"), Box::new(|param| a_obj.borrow_mut().fun1(param))); l.insert(String::from("def"), Box::new(|param| a_obj.borrow_mut().fun2(param))); let key1 = String::from("abc"); let h1 = l.get_mut(&key1).unwrap(); h1(3); let key2 = String::from("def"); let h2 = l.get_mut(&key2).unwrap(); h2(686); }
关键修改点
- 导入
std::cell::RefCell模块 - 将
a_obj声明为RefCell<A>,无需mut修饰,内部可变性由RefCell管理 - 闭包中通过
a_obj.borrow_mut()临时获取可变引用,每次调用闭包时才创建借用,避免了同时持有多个可变引用的情况
运行结果
执行后会输出:
fun1: 3, 9 fun2: 686, 9
内容的提问来源于stack exchange,提问作者Harry

