Iterator with shared mutable hash map

I need a mutable shared hash map and can't use Rc::get_mut, so I changed the structure of my SharedMap to contain Rc<RefCell<HashMap<K, V>>> instead of simply Rc<HashMap<K, V>>, since RefCell::borrow_mut() does not require a mutable base.

I'm getting compilation error since I'm always borrowing a temporary value at the iter() method. Rust Playground

I gave up on iteration and ended up with this :frowning:

pub fn entries(&self) -> Vec<(K, V)> where K: Clone, V: Clone {
    self.0.borrow().iter().map(|(k, v)| (k.clone(), v.clone())).collect::<Vec<_>>()
}

pub fn keys(&self) -> Vec<K> where K: Clone {
    self.0.borrow().iter().map(|(k, _)| k.clone()).collect::<Vec<_>>()
}

pub fn values(&self) -> Vec<V> where V: Clone {
    self.0.borrow().iter().map(|(_, v)| v.clone()).collect::<Vec<_>>()
}

The Iterator trait can only support Items which can exist after the iterator drops, which isn't compatible with the iterator itself holding a read guard like Ref while handing out borrows obtained through the guard.

Even with a lending iterator trait, I don't think this is possible in a single return without being effectively self-referencial, or rather inefficient.

If it's possible to reasonably track iterator state yourself, it's still reasonable to implement a lending iterator; e.g. if this was a Vec you just need the offset and you can reborrow from the guard every call to next and just use indexing. But with HashMap, at least the standard one, there's no way to jump into the middle of an iteration in a way you can be sure is efficient. (skip can do it inefficiently,[1] assuming iteration order is consistent between modifications (I didn't check if this is guaranteed)).


  1. could technically be specialized, but even if so I'm sure it's not guaranteed ↩ī¸Ž

1 Like

You could expose a SharedMap::borrow method that can be called by other methods needing to read the map, including for iteration. The Ref returned by that method has to be in scope during the iteration. And SharedMapIterator is no longer needed.

Adding a borrow method is just a workaround, since it exposes your internal state. However, if you only need this method within the same module as SharedMap then you could make it a private method or just call self.0.borrow() directly whenever you need to.

// ...
impl<K, V> SharedMap<K, V> {
    pub fn borrow(&self) -> Ref<HashMap<K, V>> {
        self.0.borrow()
    }
// ...
    pub fn clone_content(&self) -> Self where K: Clone + Eq + Hash, V: Clone {
        let mut r = Self::new();
        let map = self.borrow();
        for (k, v) in map.iter() {
            r.set(k.clone(), v.clone());
        }
        r
    }
// ...
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.