如何在Rust中让多个对象可变访问同一中心数据池?
问题描述
我们拥有Hello和Hi两个结构体,调用它们任意一个的方法时,需要让中心State实例的计数x自增1。
以下是初始示例代码:
struct State{ x: i32 } impl State { fn new() -> Self{ Self {x:0} } fn plusplus(&mut self){ self.x += 1 } } struct Hello<'a>{ s: &'a mut State } impl Hello<'_>{ fn hello(&mut self){ println!("hello"); self.s.plusplus(); } } struct Hi<'a>{ s: &'a mut State } impl Hi<'_>{ fn hi(&mut self){ println!("hi"); self.s.plusplus(); } } fn main() { let mut s = State::new(); let mut a = Hello{s:&mut s}; let mut b = Hello{s:&mut s}; a.hello(); a.hello(); }
当前代码无法编译,原因是Rust的编译期借用规则不允许对同一个变量进行多次可变借用。
解决方案:无需使用
unsafe 完全不需要借助unsafe关键字,Rust标准库提供了安全的内部可变性工具,可根据运行场景(单线程/多线程)选择合适的方案:
方案1:单线程场景 - 使用RefCell
RefCell是单线程下的内部可变性容器,它将借用规则的检查从编译期推迟到运行期,允许在持有不可变引用的同时修改内部数据。
修改后的代码:
use std::cell::RefCell; struct State { x: i32 } impl State { fn new() -> Self { Self { x: 0 } } fn plusplus(&mut self) { self.x += 1; println!("当前计数: {}", self.x); } } struct Hello<'a> { s: &'a RefCell<State> } impl Hello<'_> { fn hello(&self) { println!("hello"); // 通过borrow_mut()获取可变引用,运行时检查借用合法性 self.s.borrow_mut().plusplus(); } } struct Hi<'a> { s: &'a RefCell<State> } impl Hi<'_> { fn hi(&self) { println!("hi"); self.s.borrow_mut().plusplus(); } } fn main() { let s = RefCell::new(State::new()); // 可以创建多个共享同一个RefCell的实例 let a = Hello { s: &s }; let b = Hi { s: &s }; a.hello(); a.hello(); b.hi(); }
方案2:多线程场景 - 使用Arc+Mutex
如果代码需要在多线程环境下运行,Mutex可以提供互斥锁保证同一时间只有一个线程能修改State,配合Arc实现多线程下的共享所有权,完全消除竞态条件。
修改后的代码:
use std::sync::{Arc, Mutex}; use std::thread; struct State { x: i32 } impl State { fn new() -> Self { Self { x: 0 } } fn plusplus(&mut self) { self.x += 1; println!("当前计数: {}", self.x); } } struct Hello { s: Arc<Mutex<State>> } impl Hello { fn hello(&self) { println!("hello"); // 获取锁,同一时间只有一个线程能进入临界区 let mut state = self.s.lock().unwrap(); state.plusplus(); } } struct Hi { s: Arc<Mutex<State>> } impl Hi { fn hi(&self) { println!("hi"); let mut state = self.s.lock().unwrap(); state.plusplus(); } } fn main() { let s = Arc::new(Mutex::new(State::new())); // 通过Arc::clone复制共享所有权 let a = Hello { s: Arc::clone(&s) }; let b = Hi { s: Arc::clone(&s) }; // 在不同线程中调用方法 let handle1 = thread::spawn(move || { a.hello(); a.hello(); }); let handle2 = thread::spawn(move || { b.hi(); b.hi(); }); handle1.join().unwrap(); handle2.join().unwrap(); }
总结
无论是单线程还是多线程场景,都可以通过Rust标准库提供的安全工具解决问题,完全不需要使用unsafe代码。
内容的提问来源于stack exchange,提问作者Mr User
相关产品推荐
相关产品推荐

