You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中如何持有多个可变引用?ArrayMap遇BorrowMutError求解

针对Rust ArrayRef的RefCell相关问题的可行方案

问题根源梳理

  1. RefCell的BorrowMutError:如果你的get方法返回&mut T,第一次调用会获取RefCell的可变借用,第二次调用时会尝试再次获取排他性的可变借用,直接触发panic——这是RefCell的安全机制,保证同一时间只能有一个可变借用。
  2. Rc的E0515错误:本质是你返回了临时Rc<RefCell>实例的引用,或者从临时借用中返回了引用,导致返回值的生命周期无法匹配函数签名要求。

方案1:调整设计,返回不可变引用(优先推荐)

如果业务场景允许不需要可变引用,直接返回不可变引用即可,RefCell允许多个并行的不可变借用,完全避免冲突:

use std::cell::RefCell;

struct ArrayMap<K, V> {
    keys: Vec<K>,
    values: Vec<V>,
}

impl<K: PartialEq, V> ArrayMap<K, V> {
    fn new() -> Self {
        Self { keys: Vec::new(), values: Vec::new() }
    }

    fn insert(&mut self, key: K, value: V) {
        if let Some(idx) = self.keys.iter().position(|k| k == &key) {
            self.values[idx] = value;
        } else {
            self.keys.push(key);
            self.values.push(value);
        }
    }

    // 返回不可变引用,支持多次调用
    fn get(&self, key: &K) -> Option<&V> {
        self.keys.iter().position(|k| k == key).map(|idx| &self.values[idx])
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_multiple_get() {
        let mut map = ArrayMap::new();
        map.insert("a", 1);
        map.insert("b", 2);
        
        let a = map.get(&"a").unwrap();
        let b = map.get(&"b").unwrap();
        assert_eq!(*a, 1);
        assert_eq!(*b, 2);
    }
}

方案2:用RefMut封装可变引用(安全的InteriorMutability用法)

如果必须返回可变引用,不要直接返回&mut V,而是返回RefCell提供的RefMut类型——它会自动管理借用生命周期,在实例被drop时释放借用,避免重复借用panic:

use std::cell::{RefCell, RefMut};

struct ArrayMap<K, V> {
    keys: Vec<K>,
    values: RefCell<Vec<V>>,
}

impl<K: PartialEq, V> ArrayMap<K, V> {
    fn new() -> Self {
        Self { keys: Vec::new(), values: RefCell::new(Vec::new()) }
    }

    fn insert(&mut self, key: K, value: V) {
        let mut values = self.values.borrow_mut();
        if let Some(idx) = self.keys.iter().position(|k| k == &key) {
            values[idx] = value;
        } else {
            self.keys.push(key);
            values.push(value);
        }
    }

    // 返回RefMut而非直接的&mut V
    fn get_mut(&self, key: &K) -> Option<RefMut<'_, V>> {
        let values = self.values.borrow_mut();
        let idx = self.keys.iter().position(|k| k == key)?;
        Some(RefMut::map(values, |v| &mut v[idx]))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_multiple_get_mut() {
        let mut map = ArrayMap::new();
        map.insert("a", 1);
        map.insert("b", 2);
        
        // 不可同时持有多个RefMut,但可依次使用
        {
            let mut a = map.get_mut(&"a").unwrap();
            *a += 1;
        } // a被drop,释放借用
        
        let mut b = map.get_mut(&"b").unwrap();
        *b += 2;
        
        assert_eq!(*map.get_mut(&"a").unwrap(), 2);
        assert_eq!(*map.get_mut(&"b").unwrap(), 4);
    }
}

方案3:UnsafeCell手动管理借用(谨慎使用)

如果你能100%保证自己遵守Rust的借用规则(无数据竞争、无悬垂引用),可以用UnsafeCell绕过安全检查,但这会失去Rust的安全保障,仅适合极端场景:

use std::cell::UnsafeCell;

struct ArrayMap<K, V> {
    keys: Vec<K>,
    values: UnsafeCell<Vec<V>>,
}

// 单线程场景下手动实现Sync(跨线程需额外保证线程安全)
unsafe impl<K: Sync, V: Sync> Sync for ArrayMap<K, V> {}

impl<K: PartialEq, V> ArrayMap<K, V> {
    fn new() -> Self {
        Self { keys: Vec::new(), values: UnsafeCell::new(Vec::new()) }
    }

    fn insert(&mut self, key: K, value: V) {
        let values = unsafe { &mut *self.values.get() };
        if let Some(idx) = self.keys.iter().position(|k| k == &key) {
            values[idx] = value;
        } else {
            self.keys.push(key);
            values.push(value);
        }
    }

    // 手动保证同一时间只有一个可变引用存在
    fn get_mut(&self, key: &K) -> Option<&mut V> {
        let idx = self.keys.iter().position(|k| k == key)?;
        unsafe {
            let values = &mut *self.values.get();
            Some(&mut values[idx])
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_multiple_get_mut() {
        let mut map = ArrayMap::new();
        map.insert("a", 1);
        map.insert("b", 2);
        
        // 必须保证同一时间只有一个可变引用
        let mut a = map.get_mut(&"a").unwrap();
        *a += 1;
        drop(a); // 显式释放借用
        
        let mut b = map.get_mut(&"b").unwrap();
        *b += 2;
        
        assert_eq!(*map.get_mut(&"a").unwrap(), 2);
        assert_eq!(*map.get_mut(&"b").unwrap(), 4);
    }
}

内容的提问来源于stack exchange,提问作者tkbsm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 13:45:37