Rust借用错误求解:无法多次可变借用及可变与不可变借用冲突
问题描述
我写了这段Rust代码:
let mut errors: Vec<String> = Vec::new(); let msg1 = "message 1".to_string(); let msg2 = "message 2".to_string(); let msg3 = "message 3".to_string(); let mut push_auth_requirements = || { errors.push(msg1.clone()); errors.push(msg2.clone()); }; let mut push_auth_error = || { errors.push(msg3.clone()); push_auth_requirements(); }; // do some stuff if errors.is_empty() { // do more stuff if "bob" == "dsadasd" { push_auth_requirements(); } else { push_auth_error(); } } println!("Errors: {:?}",errors);
运行后报了两个错误:
error[E0499]: cannot borrow `errors` as mutable more than once at a time --> src/main.rs:89:31 | 84 | let mut push_auth_requirements = || { | -- first mutable borrow occurs here 85 | errors.push(msg1.clone()); | ------ first borrow occurs due to use of `errors` in closure ... 89 | let mut push_auth_error = || { | ^^ second mutable borrow occurs here 90 | errors.push(msg3.clone()); | ------ second borrow occurs due to use of `errors` in closure 91 | push_auth_requirements(); | ---------------------- first borrow later captured here by closure error[E0502]: cannot borrow `errors` as immutable because it is also borrowed as mutable --> src/main.rs:96:8 | 84 | let mut push_auth_requirements = || { | -- mutable borrow occurs here 85 | errors.push(msg1.clone()); | ------ first borrow occurs due to use of `errors` in closure ... 96 | if errors.is_empty() { | ^^^^^^ immutable borrow occurs here ... 99 | push_auth_requirements(); | ---------------------- mutable borrow later used here
我想让两个闭包都能修改errors这个向量,该怎么解决这些错误?
解决方法
这是Rust借用检查器在起作用——它不允许同时存在多个可变引用,也不允许可变引用和不可变引用同时存在。要让多个闭包共享并修改同一个向量,得用内部可变性特性,再结合共享所有权的工具。
单线程场景:用Rc<RefCell<Vec<String>>>
Rc能让多个变量共享同一个值的所有权,RefCell则把借用检查从编译期移到运行期,允许我们在持有不可变引用的情况下修改内部数据。
改完的代码是这样的:
use std::rc::Rc; use std::cell::RefCell; fn main() { // 把普通Vec包进Rc和RefCell里 let errors = Rc::new(RefCell::new(Vec::new())); let msg1 = "message 1".to_string(); let msg2 = "message 2".to_string(); let msg3 = "message 3".to_string(); // 克隆Rc指针,给第一个闭包用 let errors_clone1 = Rc::clone(&errors); let mut push_auth_requirements = move || { // 用borrow_mut()获取可变引用,修改内部向量 errors_clone1.borrow_mut().push(msg1.clone()); errors_clone1.borrow_mut().push(msg2.clone()); }; // 再克隆一个Rc给第二个闭包 let errors_clone2 = Rc::clone(&errors); let mut push_auth_error = move || { errors_clone2.borrow_mut().push(msg3.clone()); push_auth_requirements(); }; // do some stuff // 用borrow()获取不可变引用,检查是否为空 if errors.borrow().is_empty() { // do more stuff if "bob" == "dsadasd" { push_auth_requirements(); } else { push_auth_error(); } } // 打印时同样用borrow()获取不可变引用 println!("Errors: {:?}", errors.borrow()); }
多线程场景:用Arc<Mutex<Vec<String>>>
如果你的代码要在多线程里跑,就把Rc<RefCell>换成Arc<Mutex>——Arc是线程安全的共享所有权工具,Mutex保证同一时间只有一个线程能访问内部数据:
use std::sync::{Arc, Mutex}; fn main() { let errors = Arc::new(Mutex::new(Vec::new())); let msg1 = "message 1".to_string(); let msg2 = "message 2".to_string(); let msg3 = "message 3".to_string(); let errors_clone1 = Arc::clone(&errors); let mut push_auth_requirements = move || { // 用lock()获取锁,得到可变引用 let mut err = errors_clone1.lock().unwrap(); err.push(msg1.clone()); err.push(msg2.clone()); }; let errors_clone2 = Arc::clone(&errors); let mut push_auth_error = move || { let mut err = errors_clone2.lock().unwrap(); err.push(msg3.clone()); // 必须先释放锁,再调用另一个闭包,不然会死锁 drop(err); push_auth_requirements(); }; // do some stuff if errors.lock().unwrap().is_empty() { // do more stuff if "bob" == "dsadasd" { push_auth_requirements(); } else { push_auth_error(); } } println!("Errors: {:?}", errors.lock().unwrap()); }
注意多线程里调用push_auth_requirements前一定要先释放当前的锁,不然两个闭包会互相等对方释放锁,导致死锁。
内容的提问来源于stack exchange,提问作者sudoExclamationExclamation
相关产品推荐
相关产品推荐

