How do I create a HashMap literal?
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)
]);
}
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:
- 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.
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();
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.