Creating HashMap in Rust: from pain to beauty in 2025
\n\nIf you've ever written in Rust and wondered "How do I create a HashMap literal?" — you're not alone. This question on Stack Overflow has gathered thousands of views, and for good reason. In languages like Python or JavaScript, creating a dictionary is literally a pair of curly braces. In Rust, it's a bit more complicated… but only at first glance.
\n\nIn 2025, the Rust ecosystem has come a long way. New macros have appeared, constant handling has improved, and the compiler has become even smarter. In this article, we'll cover all ways to create a HashMap, from classic to the most modern. You'll learn how to write not just working, but beautiful and efficient code.
\n\nLet's go! 🦀
\n\nWhy doesn't Rust have a built-in HashMap literal?
\n\nThis is probably the most common question from beginners. The answer is simple: Rust is not Python. Rust has no "magic" at the syntax level for collections because the language strives for predictability and memory control.
\n\nImagine you write:
\n\nlet map = {"key1": "value1", "key2": "value2"};\n\nLooks logical? Yes. But what if the keys or values are not strings, but some complex structures? How does the compiler know which allocator to use? And what about hashing? Rust leaves these decisions to the developer.
\n\nThat's why the standard library uses HashMap::new() and .insert() methods to create a HashMap. But, you'll agree, writing 10 lines of code to create a dictionary of three pairs is painful. Fortunately, there are solutions.
Method #1: Classic — HashMap::new() + insert()
\n\nLet's start with the basics. This is the most understandable and transparent way. You create an empty HashMap and fill it:
\n\nuse std::collections::HashMap;\n\nfn main() {\n let mut scores = HashMap::new();\n scores.insert("Alice".to_string(), 42);\n scores.insert("Bob".to_string(), 27);\n scores.insert("Charlie".to_string(), 35);\n\n println!("{:?}", scores);\n}\n\nPros:
\n- \n
- Full control over the process. \n
- Can add elements conditionally. \n
- No additional dependencies required. \n
Cons:
\n- \n
- Verbose. \n
- Requires
mut. \n - Not suitable for constants. \n
This method is good for dynamic filling, but for static data it's overkill.
\n\nMethod #2: from() — when data is already ready
\n\nIf you have a vector of tuples, you can use HashMap::from():
use std::collections::HashMap;\n\nfn main() {\n let data = vec![\n ("Alice", 42),\n ("Bob", 27),\n ("Charlie", 35),\n ];\n\n let scores: HashMap<&str, i32> = HashMap::from(data);\n println!("{:?}", scores);\n}\n\nThis is closer to a "literal". Looks cleaner, but still requires an intermediate vector. It works fast, but for large datasets it can be inefficient due to double memory allocation.
\n\nMethod #3: collect() — functional approach
\n\nFans of iterators will like this method:
\n\nuse std::collections::HashMap;\n\nfn main() {\n let scores: HashMap<&str, i32> = [\n ("Alice", 42),\n ("Bob", 27),\n ("Charlie", 35),\n ].iter().cloned().collect();\n\n println!("{:?}", scores);\n}\n\nBeautiful? Yes. Readable? Barely. But this is one of the most idiomatic ways in Rust. .iter().cloned().collect() is a standard pattern for converting an array into a collection.
By the way, if your keys and values implement Copy, you can use .copied() instead of .cloned() — it's slightly faster.
Method #4: hashmap! macro — salvation from 2025
\n\nAnd here