Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create a HashMap literal?

Tags:

hashmap

rust

People also ask

How do you directly initialize a HashMap in a literal way?

This is one way. Map<String, String> h = new HashMap<String, String>() {{ put("a","b"); }};

How do you initialize a HashMap while declaring it?

The Static Initializer for a Static HashMap We can also initialize the map using the double-brace syntax: Map<String, String> doubleBraceMap = new HashMap<String, String>() {{ put("key1", "value1"); put("key2", "value2"); }};

How do you populate a HashMap in Java?

To populate HashMap in previous Java versions, we have to use the put() method. You can also make a map immutable once you populate the static HashMap. If we try to modify the immutable Map, we will get java. lang.


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.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:

  • Add hashmap, hashset, treemap, and treeset macros

Previous versions

You can create a macro to do the job for you, as demonstrated in Why does this rust HashMap macro no longer work?. 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.


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.

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();

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)
    ]);
}

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();

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),
]);

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.