How do I create a HashMap literal?

HashmapRust

Hashmap Problem Overview


How I can create a HashMap literal in Rust? In Python I can do it so:

hashmap = { 'element0': { 'name': 'My New Element', 'childs': { 'child0': { 'name': 'Child For Element 0', 'childs': { ... } } } }, ... }

And in Go like this:

type Node struct { name string childs map[string]Node }

hashmap := map[string]Node {
    "element0": Node{
        "My New Element",
        map[string]Node {
            'child0': Node{
                "Child For Element 0",
                map[string]Node {}
            }
        }
    }
}

Hashmap Solutions


Solution 1 - Hashmap

There isn't a map literal syntax in Rust. I don't know the exact reason, but I expect that the fact that there are multiple data structures that act maplike (such as both BTreeMap and HashMap) would make it hard to pick one.

Rust 1.56

Many collections now offer conversions from an array argument using From or Into:

use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};

fn main() {
    let s = Vec::from([1, 2, 3]);
    println!("{:?}", s);

    let s = BTreeSet::from([1, 2, 3]);
    println!("{:?}", s);

    let s = HashSet::from([1, 2, 3]);
    println!("{:?}", s);

    let s = BTreeMap::from([(1, 2), (3, 4)]);
    println!("{:?}", s);

    let s = HashMap::from([(1, 2), (3, 4)]);
    println!("{:?}", s);
}

This logic can be wrapped back into a macro for some syntax sugar:

use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};

macro_rules! collection {
    // map-like
    ($($k:expr => $v:expr),* $(,)?) => {{
        core::convert::From::from([$(($k, $v),)*])
    }};
    // set-like
    ($($v:expr),* $(,)?) => {{
        core::convert::From::from([$($v,)*])
    }};
}

fn main() {
    let s: Vec<_> = collection![1, 2, 3];
    println!("{:?}", s);

    let s: BTreeSet<_> = collection! { 1, 2, 3 };
    println!("{:?}", s);

    let s: HashSet<_> = collection! { 1, 2, 3 };
    println!("{:?}", s);

    let s: BTreeMap<_, _> = collection! { 1 => 2, 3 => 4 };
    println!("{:?}", s);

    let s: HashMap<_, _> = collection! { 1 => 2, 3 => 4 };
    println!("{:?}", s);
}

Rust 1.51

As of Rust 1.51, you can use by-value array iterators and FromIterator to collect into many kinds of collections:

use std::array::IntoIter;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::iter::FromIterator;

fn main() {
    // Rust 1.53
    let s = Vec::from_iter([1, 2, 3]);
    println!("{:?}", s);

    // Rust 1.51
    let s = Vec::from_iter(IntoIter::new([1, 2, 3]));
    println!("{:?}", s);

    let s = BTreeSet::from_iter(IntoIter::new([1, 2, 3]));
    println!("{:?}", s);

    let s = HashSet::<_>::from_iter(IntoIter::new([1, 2, 3]));
    println!("{:?}", s);

    let s = BTreeMap::from_iter(IntoIter::new([(1, 2), (3, 4)]));
    println!("{:?}", s);

    let s = HashMap::<_, _>::from_iter(IntoIter::new([(1, 2), (3, 4)]));
    println!("{:?}", s);
}

Note that in Rust 1.53, std::array::IntoIter isn't always needed.

This logic can be wrapped back into a macro for some syntax sugar:

use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};

macro_rules! collection {
    // map-like
    ($($k:expr => $v:expr),* $(,)?) => {{
        use std::iter::{Iterator, IntoIterator};
        Iterator::collect(IntoIterator::into_iter([$(($k, $v),)*]))
    }};
    // set-like
    ($($v:expr),* $(,)?) => {{
        use std::iter::{Iterator, IntoIterator};
        Iterator::collect(IntoIterator::into_iter([$($v,)*]))
    }};
}

fn main() {
    let s: Vec<_> = collection![1, 2, 3];
    println!("{:?}", s);

    let s: BTreeSet<_> = collection! { 1, 2, 3 };
    println!("{:?}", s);

    let s: HashSet<_> = collection! { 1, 2, 3 };
    println!("{:?}", s);

    let s: BTreeMap<_, _> = collection! { 1 => 2, 3 => 4 };
    println!("{:?}", s);

    let s: HashMap<_, _> = collection! { 1 => 2, 3 => 4 };
    println!("{:?}", s);
}

These solutions avoid both unneeded allocation and reallocation.

See also:

Previous versions

You can create a macro to do the job for you, as demonstrated in https://stackoverflow.com/questions/24276617. Here is that macro simplified a bit and with enough structure to make it runnable in the playground:

macro_rules! map(
    { $($key:expr => $value:expr),+ } => {
        {
            let mut m = ::std::collections::HashMap::new();
            $(
                m.insert($key, $value);
            )+
            m
        }
     };
);

fn main() {
    let names = map!{ 1 => "one", 2 => "two" };
    println!("{} -> {:?}", 1, names.get(&1));
    println!("{} -> {:?}", 10, names.get(&10));
}

This macro avoids allocating an unneeded intermediate Vec, but it doesn't use HashMap::with_capacity so there may be some useless reallocations of the HashMap as values are added. A more complicated version of the macro that counts the values is possible, but the performance benefits are probably not something most uses of the macro would benefit from.

Solution 2 - Hashmap

I recommend the maplit crate.

To quote from the documentation:

> Macros for container literals with specific type.

use maplit::hashmap;

let map = hashmap!{
    "a" => 1,
    "b" => 2,
};

> The maplit crate uses => syntax for the mapping macros. It is not possible to use : as separator due to syntactic the restrictions in regular macro_rules! macros. > > Note that rust macros are flexible in which brackets you use for the invocation. You can use them as hashmap!{} or hashmap![] or hashmap!(). This crate suggests {} as the convention for the map & set macros, it matches their Debug output. > > Macros > * btreemap Create a BTreeMap from a list of key-value pairs > * btreeset Create a BTreeSet from a list of elements. > * hashmap Create a HashMap from a list of key-value pairs > * hashset Create a HashSet from a list of elements.

Solution 3 - Hashmap

There is an example of how to achieve this in the documentation for HashMap:

let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)]
    .iter()
    .cloned()
    .collect();

Solution 4 - Hashmap

Starting with Rust 1.56, you can initialize a HashMap using from(), which is somewhat like having a HashMap literal. from() takes an array of key-value pairs. You can use it like this:

use std::collections::HashMap;

fn main() {
    let hashmap = HashMap::from([
        ("foo", 1),
        ("bar", 2)
    ]);
}

Solution 5 - Hashmap

As noted by @Johannes in the comments, it's possible to use vec![] because:

  • Vec<T> implements the IntoIterator<T> trait
  • HashMap<K, V> implements FromIterator<Item = (K, V)>

which means you can do this:

let map: HashMap<String, String> = vec![("key".to_string(), "value".to_string())]
    .into_iter()
    .collect();

You can use &str but you might need to annotate lifetimes if it's not 'static:

let map: HashMap<&str, usize> = vec![("one", 1), ("two", 2)].into_iter().collect();

Solution 6 - Hashmap

You can use the velcro crate*. It is similar to maplit, as recommended in other answers, but with more collection types, better syntax (in my opinion at least!) and more features.

Assuming you want to use Strings rather than &str, your exact example would look like this:

use std::collections::HashMap;
use velcro::hash_map;

struct Node {
    name: String
    children: HashMap<String, Node>,
}

let map = hash_map! {
    String::from("element0"): Node {
        name: "My New Element".into(),
        children: hash_map! {
            String::from("child0"): Node {
                name: "child0".into(),
                children: hash_map!{}
            }
        }
    }
};

That's a bit ugly because of how Strings are constructed. But it can be made a bit cleaner, without changing the key type, by using hash_map_from! which will automatically do conversions:

use std::collections::HashMap;
use velcro::{hash_map, hash_map_from};

let map: HashMap<String, Node> = hash_map_from! {
    "element0": Node {
        name: "My New Element".into(),
        children: hash_map_from! {
            "child0": Node {
                name: "child0".into(),
                children: hash_map!{}
            }
        }
    }
};

Which isn't much more verbose than the Go version.


*Full disclosure: I am the author of this crate.

Solution 7 - Hashmap

As of Rust 1.51, IntoIterator is implemented for arrays, so you can create a HashMap with the from_iter method without cloning:

use std::collections::HashMap;
use std::iter::FromIterator;

// note that this type annotation is required
let mut map: HashMap<_, _> = HashMap::from_iter([("a", 1), ("b", 2), ("c", 3)]);

And as of Rust 1.56 (currently nightly), you can use the From<[(K, V); N]> implementation, which is even cleaner:

let mut map = HashMap::from([
    ("a", 1),
    ("b", 2),
    ("c", 3),
]);

Solution 8 - Hashmap

For one element

If you wish to initialize the map with only one element in one line (and without visible mutation in your code), you could do:

let map: HashMap<&'static str, u32> = Some(("answer", 42)).into_iter().collect();

This is thanks to the usefulness of Option being able to become an Iterator using into_iter().

In real code, you probably don't need to help the compiler with the type:

use std::collections::HashMap;

fn john_wick() -> HashMap<&'static str, u32> {
    Some(("answer", 42)).into_iter().collect()
}

fn main() {
    let result = john_wick();

    let mut expected = HashMap::new();
    expected.insert("answer", 42);

    assert_eq!(result, expected);
}

There is also a way to chain this to have more than one element doing something like Some(a).into_iter().chain(Some(b).into_iter()).collect(), but this is longer, less readable and probably has some optimization issues, so I advise against it.

Solution 9 - Hashmap

I have seen a bunch of fancy solutions, but I just wanted something simple. To that end, here is a Trait:

use std::collections::HashMap;

trait Hash {
   fn to_map(&self) -> HashMap<&str, u16>;
}

impl Hash for [(&str, u16)] {
   fn to_map(&self) -> HashMap<&str, u16> {
      self.iter().cloned().collect()
   }
}

fn main() {
   let m = [("year", 2019), ("month", 12)].to_map();
   println!("{:?}", m)
}

I think it's a good option, as it's essentially what's already used by Ruby and Nim:

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMaxim SamburskiyView Question on Stackoverflow
Solution 1 - HashmapShepmasterView Answer on Stackoverflow
Solution 2 - HashmapDavid J.View Answer on Stackoverflow
Solution 3 - Hashmapjupp0rView Answer on Stackoverflow
Solution 4 - HashmapDaniel GigerView Answer on Stackoverflow
Solution 5 - HashmapKamil TomÅ¡íkView Answer on Stackoverflow
Solution 6 - HashmapPeter HallView Answer on Stackoverflow
Solution 7 - HashmapIbraheem AhmedView Answer on Stackoverflow
Solution 8 - HashmapStargateurView Answer on Stackoverflow
Solution 9 - HashmapZomboView Answer on Stackoverflow