Search code examples
rusthashmap

How to get the length of a Rust HashMap<> that is wrapped in Arc<> and RwLock<>?


I have a simple structure with a questions variable defined as follows:

struct Store {
    questions: Arc<RwLock<HashMap<QuestionId, Question>>>,
}

Sorry if this is a primitive question but:

How can I get the length of the HashMap<> that is wrapped in RwLock<> and Arc<>?

I could not seem to find anything in the Arc documentation either (even though I know it is not responsible for tasks like this).

For some context, this is a store intended to be used by a Rest API Web Server implemented with the Tokio framework.


Solution

  • Although previous answers explain how to get HashMap's length, they do not explain why it works. I'll try to explain the latter, so you will understand this mechanism in the future.

    Calling methods of "inner" objects on the "outer" objects is possible thanks to the Deref trait and deref coercion.

    Deref trait defines semantics of the dereferencing operator *. For example when you dereference String you get str and when you dereference Vec<T> you get [T]. Similarly smart pointers like Box<T> or Arc<T> give you T when you dereference them.

    You could do this dereferencing manually:

    let map = Arc::new(RwLock::new(HashMap::from([("a", "b"), ("c", "d")])));
    let len = (&*(&*map).read().await).len();
    

    However this quickly gets very messy. Luckily to help comes deref coercion and auto-magically performs this series of dereferencing. The details of how it works are described in the linked documentation, but the gist of it is that when you call method foo on object bar: Bar the compiler will do the following:

    1. check if Bar has method foo, if not then
    2. check if Bar implements Deref<Target=Baz> and if it does then
    3. check if Baz has method foo, if not then
    4. perform steps 2 and 3 translatively for previous Deref Target until it finds type that implements method foo

    So when you write

    let len = map.read().await.len();
    

    the compiler does the following:

    1. Checks for method read on Arc<RwLock<_>> and doesn't find it.
    2. Tries to dereference it and checks for method read on RwLock<_>.
    3. It finds it, and after awaiting result you have a RwLockReadGuard<'_, HashMap<_, _>>.
    4. It looks for method len on RwLockReadGuard<_> and doesn't find it.
    5. So it dereferences it once more and you have in result HashMap<_>.
    6. Finally compiler finds type that implements len method so it calls it.