tkbsm
tkbsm

Reputation: 23

How can I hold multiple mutable references in Rust?

I'm writing the "ArrayMap" object that includes the set of key and Vec in Rust. The following are the module and the test code.

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

pub struct ArrayMap {
    map: RefCell<HashMap<String, Vec<i32>>>,
}

impl ArrayMap {
    pub fn new() -> Self {
        let map = RefCell::new(HashMap::<String, Vec<i32>>::new());
        ArrayMap{map: map}
    }

    pub fn add(&self, key: &str) {
        self.map.borrow_mut().insert(key.to_string(), Vec::<i32>::new());
    }

    pub fn get(&self, key: &str) -> RefMut<Vec<i32>> {
        let map = self.map.borrow_mut();  // panic
        RefMut::map(map, |map| map.get_mut(key).unwrap())
    }
}
mod array_map;
use array_map::ArrayMap;

fn main() {
    let m = ArrayMap::new();
    m.add("array0");
    m.add("array1");
    let _a0 = m.get("array0");
    let _a1 = m.get("array1");
}

However, the error below occurs.

thread 'main' panicked at 'already borrowed: BorrowMutError', src\array_map.rs:19:28

I know it works if I divide two m.get() into other blocks but I don't want to do so for the actual usage.

And I tried to use Rc<RefCell> for the "map" of ArrayMap.

use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::rc::Rc;

pub struct ArrayMap {
    map: Rc<RefCell<HashMap<String, Vec<i32>>>>,
}

impl ArrayMap {
    pub fn new() -> Self {
        let map = Rc::new(RefCell::new(HashMap::<String, Vec<i32>>::new()));
        ArrayMap{map: map}
    }

    pub fn add(&self, key: &str) {
        self.map.borrow_mut().insert(key.to_string(), Vec::<i32>::new());
    }

    pub fn get(&self, key: &str) -> RefMut<Vec<i32>> {
        let map = Rc::clone(&self.map).borrow_mut();
        RefMut::map(map, |map| map.get_mut(key).unwrap())  // error[E0515]
    }
}

But this error occurs.

error[E0515]: cannot return value referencing temporary value
  --> src\array_map.rs:21:9

Could you please tell me the way to avoid this situation? Any ideas are welcome.

Upvotes: 2

Views: 1011

Answers (1)

YthanZhang
YthanZhang

Reputation: 410

You cannot hold multiple RefMut at the same time, there must only be one RefMut that exist at any given time. This is enforced at runtime, and if this rule is violated, the program panics. This is what happend in the first case.

What you want is for get to return a Rc<RefCell<Vec<i32>>>, so you can hold multiple Rc to a vector and access it mutably whenever you want.

use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

pub struct ArrayMap<T> {
    map: HashMap<String, Rc<RefCell<Vec<T>>>>,
}

impl<T> ArrayMap<T> {
    pub fn new() -> Self {
        ArrayMap { map: HashMap::new() }
    }

    pub fn add(&mut self, key: &str, data: Vec<T>) {
        self.map.insert(key.to_string(), Rc::new(RefCell::new(data)));
    }

    pub fn get(&mut self, key: &str) -> Option<Rc<RefCell<Vec<T>>>> {
        self.map.get(key).cloned()
    }
}


fn main() {
    let mut am = ArrayMap::new();

    am.add("a", vec![1, 2, 3]);
    am.add("b", vec![4, 5, 6]);

    let a1 = am.get("a").unwrap();
    let a2 = am.get("a").unwrap();

    let b1 = am.get("b").unwrap();
    let b2 = am.get("b").unwrap();

    for a in a1.borrow_mut().iter_mut() {
        *a *= 2;
    }

    for b in b2.borrow_mut().iter_mut() {
        *b *= 3;
    }

    println!("{a1:?}, {a2:?}");
    println!("{b1:?}, {b2:?}")
}

Upvotes: 2

Related Questions