Rust Hash-Maps Need Better Ergonomics

September 30, 2021

I’m making my way through The Rust Programming Language and I find that my largest annoyance coming from other languages is the syntax for constructing an instance of HashMap<K, V>. What I feel should be a simple syntax, considering the prevalence of hash-map usage, is mired in boilerplate.

There are a few ways of constructing a HashMap<K, V> prior to the 1.56-beta, but I don’t particularly like any of them.

// Mutable to insert, but m1 is mutable in this scope now.
let mut m1 = HashMap::new();
m1.insert("one", 1);
m1.insert("two", 2);

// Immutable using a nested block. Kinda ugly.
let m2 = {
    let mut m = HashMap::new();
    m.insert("one", 1);
    m.insert("two", 2);
    m
};

// Can be collected from an iterator. Also ugly, especially with the type
// annotation using _.
let m3: HashMap<_, _> = [
    ("one", 1),
    ("two", 2),
].iter().cloned().collect();

Things are a little better in 1.56-beta with the From trait implementation, but this still requires an array of tuples to be created, regardless of whether or not they are optimized away.

// In the 1.56 beta there is From support.
let m4 = HashMap::from([
    ("one", 1),
    ("two", 2),
]);

I’d really prefer something similar to vec!, ideally with emphasis on ergonomics and legibility as I don’t really consider Rust’s syntax to be the easiest to read at a glance. Something like this:

// A macro like "vec!". No array or tuples. Abstracts inserts in block.
let m5 = hashmap![
    "one" => 1,
    "two" => 2,
];

// Also looks better on 1 line.
let m6 = hashmap!["one" => 1, "two" => 2];

The following macro, hopefully better prepared for production than my quick write-up, within the standard library would go a long way to fixing my annoyance.

macro_rules! hashmap {
  () => (
    HashMap::new();
  );
  ( $( $k:expr => $v:expr ),+ $(,)? ) => (
    {
      let mut temp_map = HashMap::new();
      $(
        temp_map.insert($k, $v);
      )*
      temp_map
    }
  );
}

I do think that the absence of a macro like hashmap! highlights that the language has not been focused on ergonomics. Common things should be trivial to express and I shouldn’t need to create explicit nested blocks, expose a mutable value to a larger scope, or construct arrays of tuples in order to build common data structures.