How do I create a HashMap literal?
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 {}
}
}
}
}
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 regularmacro_rules!
macros.Note that rust macros are flexible in which brackets you use for the invocation. You can use them as
hashmap!{}
orhashmap![]
orhashmap!()
. This crate suggests{}
as the convention for the map&
set macros, it matches their Debug output.Macros
btreemap
Create aBTreeMap
from a list of key-value pairsbtreeset
Create aBTreeSet
from a list of elements.hashmap
Create aHashMap
from a list of key-value pairshashset
Create aHashSet
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)
]);
}