单线程Rust应用中可变共享状态的RefCell与Arena替代方案探寻
单线程Rust应用的可变共享状态管理方案探索
需求背景
开发单线程Rust应用,需管理可变共享状态,核心要求是保留Rust编译时安全检查优势,同时避免RefCell带来的运行时恐慌。
尝试过的方案与问题
方案1:基于索引的Arena内存分配
- 思路:通过Arena进行内存分配,用索引引用数据
- 问题:对Arena中数据的可变访问要求独占Arena的引用权限,这和需要维持数据结构内元素引用的需求冲突,尤其是处理带变体特定模式的枚举类型时,场景会变得异常复杂。
方案2:Rc+Weak引用计数组合
- 思路:通过引用计数实现共享所有权
- 顾虑:要实现可变访问必须配合
RefCell,这会把大量检查转移到运行时,引入恐慌风险,违背了保留编译时安全保证的目标。
当前临时解决方案
使用crossbeam的SkipMap替代常规的BTreeMap或HashMap,代码实现如下:
pub struct Arena<T> { data: SkipMap<usize, T>, next_id: Cell<usize>, } impl<T: Send + 'static> Arena<T> { pub fn new() -> Arena<T> { Arena { data: Default::default(), next_id: Cell::new(1), } } pub fn insert(&self, value: T) -> usize { // IF, IN OTHER IMPLEMENTATIONS, I WOULD INSERT A REFCELL HERE WHILE HOLDING AN ALIASING BORROW TO AN ELEMENT, PROGRAM PANICS let id = self.next_id.get(); self.next_id.set(id + 1); self.data.insert(id, value); id } pub fn get(&self, id: usize) -> Option<impl std::ops::Deref<Target = T> + '_> { struct Dummy<'a, K, V> (Entry<'a, K, V>); impl<'a, K, V> Deref for Dummy<'a, K, V> { type Target = V; fn deref(&self) -> &V { self.0.value() } } self.data.get(&id).map(|entry| Dummy(entry)) } }
该方案虽能运行,但存在明显弊端:
- 引入
Send + 'static约束,限制了可存储类型的范围 - 并发数据结构专为多线程场景设计,单线程下会产生不必要的开销,比如原子操作、基于epoch的回收机制
- 尝试过DashMap等其他并发映射,因底层锁机制出现死锁问题,本质和
RefCell的恐慌风险类似,只是把恐慌风险转化为死锁风险。
核心问题
- 是否存在适配上述需求的Rust替代策略或数据结构?
- 如何实现一个无需独占借用、也不需要
RefCell运行时检查,就能对元素进行可变访问的Arena? - 是否有针对单线程环境优化的无锁映射可用?
内容的提问来源于stack exchange,提问作者Jhowa
相关产品推荐
相关产品推荐

