rusthashmap

Why do I have to use &char instead of char to index a key in a HashMap<char, i32>?


In the following snippet, given the type of 'a' is char, why can't I print letters['a']?

use std::collections::HashMap;

fn main() {
    let mut letters = HashMap::new();

    for ch in "a short treatise on fungi".chars() {
        letters.entry(ch).and_modify(|counter| *counter += 1).or_insert(1);
    }
    
    println!("{}", letters[&'a']);
}

I have tried to print the type of letters, which is std::collections::hash::map::HashMap<char, i32>, the type of 'a', which is char, and the type of &'a', which is &char.


Solution

  • In Rust, the subscript operator [] is implemented using the Index trait. Index is generic over the type doing the indexing, so impl Index<usize> for Type means any Type value can be indexed with any usize value.

    The impl for HashMap looks like this:

    impl<K, Q, V, S> Index<&Q> for HashMap<K, V, S>
    where
        K: Eq + Hash + Borrow<Q>,
        Q: Eq + Hash + ?Sized,
        S: BuildHasher,
    

    K is the key type in the HashMap, and the indexing type is &Q. These are linked by K: Borrow<Q>. We know K is char, so for which Q does K implement Borrow<Q>?

    impl<T> Borrow<T> for T
    where
        T: ?Sized,
    

    Here, T is char, so the only implementation is impl Borrow<char> for char.

    This is done because we want an extensible way to index, for example, HashMap<String, _> with &str, since String may be expensive to create. But unfortunately, this means we can't also have impl Index<K> for HashMap<K, _>. So we're stuck borrowing every index, whether it makes sense or not.