How do I create a HashMap literal?
Solution 1
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 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.
Solution 2
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.
Solution 3
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
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
As noted by @Johannes in the comments, it's possible to use vec![]
because:
Vec<T>
implements theIntoIterator<T>
traitHashMap<K, V>
implementsFromIterator<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();
Related videos on Youtube
![Maxim Samburskiy](https://i.stack.imgur.com/ABb2u.jpg?s=256&g=1)
Maxim Samburskiy
Updated on March 12, 2022Comments
-
Maxim Samburskiy over 2 years
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 {} } } } }
-
DK. over 9 yearsThere's also one in the
grabbag_macros
crate. You can see the source here: github.com/DanielKeep/rust-grabbag/blob/master/grabbag_macros/…. -
Shepmaster about 6 yearsWhile it works in this case, it becomes needlessly expensive when using something like
String
instead of&str
. -
Alexander about 6 yearsAh, shame. I like Swift approach to this, which abstracts literals from their types. A
DictionaryLiteral
can be used to initialize any type that conforms toExpressibleByDictionaryLiteral
(even though the standard library offers one such type,Dictionary
) -
jupp0r over 5 yearsSure there are runtime costs, but there are also costs associated with adding another crate dependency or with defining hard-to-understand macros. Most code is not performance critical and I find this version very readable.
-
Hutch Moore over 4 yearsAdditionally, this doesn't work for types that aren't Clone.
-
Johannes over 4 yearsThis example can be tuned to avoid those issues, by using
vec![ (name, value) ].into_iter().collect()
-
alex elias about 4 yearsWill this not represent the vector literal in the binary then collect it into a hashmap at runtime?
-
Stargateur over 3 years"I think its a good option, as its essentially whats already used by Ruby and Nim" I don't understand how this is a argument ? why ruby or Nim do this would be a better argument then just "they do that so it's good"
-
Stargateur over 3 years@jupp0r why bother write in Rust if you doesn't want performance ? I don't get this. You don't need to understand the detail implementation of a macro to use it.
-
Stargateur over 3 yearsan opinion without argument is not useful in a SO answer
-
Denys Séguret over 3 yearsA detail: calling the trait "Hash" is a bad idea: there's already a trait with that name and you'll fast encounter it when working with hashing in rust.
-
Stargateur over 3 yearsI would also advice to prefer
copied()
to disallow type that don't implement copy, avoiding cloning for nothing, or at least make it explicit using a cloned_to_map() if you also want to be able to do that with only cloned type. -
jupp0r over 3 years@Stargateur because I don't do premature optimization and neither should you. It might be that your program would benefit from more optimized HashMap literal initialization, but it probably won't. If you want to get the best performance, measure and optimize the parts of your program that are on the critical path. Randomly optimizing stuff is just a bad strategy. My answer will be fine for 99.9999% of people. It's readable, compiles fast, does not introduce dependencies that increase complexity and introduce potential security vulnerabilities.
-
Stargateur over 3 years@jupp0r "premature optimization" is not that
-
avl_sweden over 3 yearsYeah, a syntax like hashmap![("Norway",100)] would make things more readable and would absolutely not be a sort of "premature optimization". It's even shorter to write! It really should be in the std-lib I think.
-
jupp0r over 3 years@Stargateur optimizing anything without data-driven evidence that the optimization will yield tangible results is premature.
-
Alex over 3 yearsWith the stabilization of
std::array::IntoIter
in Rust 1.51, you no longer need nightly for the second method. -
algmyr over 2 yearsThis isn't really about prematurely optimizing, it's about not prematurely pessimizing. elegantchaos.com/2014/10/27/…
-
jupp0r over 2 yearsAdding a new dependency to your project just to declare a hashmap literal is prematurely optimizing.
-
Haider over 2 yearsWorth mentioning Rust 1.56's
HashMap::from([ ("k1",1), ("k2",2) ])