HashMap in Rust 2025: literals, macros, and best practices

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Creating HashMap in Rust: from pain to beauty in 2025

\n\n

If 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\n

In 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\n

Let's go! 🦀

\n\n

Why doesn't Rust have a built-in HashMap literal?

\n\n

This 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\n

Imagine you write:

\n\n
let map = {"key1": "value1", "key2": "value2"};
\n\n

Looks 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\n

That'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.

\n\n

Method #1: Classic — HashMap::new() + insert()

\n\n

Let's start with the basics. This is the most understandable and transparent way. You create an empty HashMap and fill it:

\n\n
use 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\n

Pros:

\n
    \n
  • Full control over the process.
  • \n
  • Can add elements conditionally.
  • \n
  • No additional dependencies required.
  • \n
\n\n

Cons:

\n
    \n
  • Verbose.
  • \n
  • Requires mut.
  • \n
  • Not suitable for constants.
  • \n
\n\n

This method is good for dynamic filling, but for static data it's overkill.

\n\n

Method #2: from() — when data is already ready

\n\n

If you have a vector of tuples, you can use HashMap::from():

\n\n
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\n

This 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\n

Method #3: collect() — functional approach

\n\n

Fans of iterators will like this method:

\n\n
use 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\n

Beautiful? 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.

\n\n

By the way, if your keys and values implement Copy, you can use .copied() instead of .cloned() — it's slightly faster.

\n\n

Method #4: hashmap! macro — salvation from 2025

\n\n

And here

Blogs

Book Recommendations