1

I have data that can be divided into several categories and each category can divided into several sub-types. I save the data as HashMap<Type1, HashMap<Type2, Data>>.

I need to get a mutable reference of different categories at the same time, so the definition becomes HashMap<Type1, RefCell<HashMap<Type2, Data>>>.

How do I implement the get function?

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

struct Foo<T> {
    data: HashMap<u32, RefCell<HashMap<u32, T>>>,
}

impl<T> Foo<T> {
    fn get_mut(&self, k: u32, k2: u32) -> Option<RefMut<T>> {
        unimplemented!() // Help me >_<
    }
}

The key question is when I call HashMap::get_mut function on RefMut<HashMap<K,V>>, it sees that there is not way to return Option<RefMut<V>>

use std::cell::RefMut;
use std::collections::HashMap;

//I can check twice if the map contains k, but it's inefficient.
fn get_mut<V>(map: RefMut<HashMap<u32, V>>, k: u32) -> Option<RefMut<V>> {
    if map.contains_key(&k) {
        Some(RefMut::map(map, |map| map.get_mut(&k).unwrap()))
    } else {
        None
    }
}
Archeus
  • 72
  • 1
  • 7
  • It looks like your question might be answered by the answers of [How do I return a reference to something inside a RefCell without breaking encapsulation?](https://stackoverflow.com/q/29401626/155423) and [Unwrap and access T from an Option>>](https://stackoverflow.com/q/54012660/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Nov 04 '19 at 15:42

1 Answers1

2

It looks like there was a method to do exactly what you are hoping for, but it was removed. However, I think this is a use case that they might not have considered, so it might not be a bad idea to try pinging the issue.

Otherwise, here is a somewhat inefficient solution that will work:

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

struct Foo<T> {
    data: HashMap<u32, RefCell<HashMap<u32, T>>>,
}

impl<T> Foo<T> {
    fn get_entry(&self, k: u32, k2: u32) -> Option<RefMut<T>> {
        self.data
            .get(&k)
            .map(|inner| inner.borrow_mut())
            .filter(|x| x.contains_key(&k2))
            .map(|x| RefMut::map(x, |y| y.get_mut(&k2).unwrap()))
    }
}
Coder-256
  • 5,212
  • 2
  • 23
  • 51
  • Thanks for your answer. I visit the link you provided and find [unsafe code](https://github.com/rust-lang/rust/issues/27746#issuecomment-172899746) maybe the correct solution for me. Although it's really difficult to understand. – Archeus Nov 13 '19 at 03:34