HashMaps in Rust
HashMaps store key-value pairs and are perfect for when you need to look up values by a unique key. They're like dictionaries in Python or objects in JavaScript.
You create a HashMap with HashMap::new() and add entries with insert(). The standard library doesn't provide a macro like vec! for HashMaps.
use std::collections::HashMap;
fn main() {
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert("Alice".to_string(), 95);
scores.insert("Bob".to_string(), 87);
scores.insert("Charlie".to_string(), 92);
println!("{:?}", scores);
}
Try it Yourself ->
Accessing and Checking Values
Use get() to retrieve values and contains_key() to check if a key exists. Both methods return borrowed references.
use std::collections::HashMap;
fn main() {
let mut fruit_prices = HashMap::new();
fruit_prices.insert("apple", 1.50);
fruit_prices.insert("banana", 0.75);
fruit_prices.insert("cherry", 3.00);
match fruit_prices.get("apple") {
Some(price) => println!("Apples cost ${:.2}", price),
None => println!("No apples found"),
}
if fruit_prices.contains_key("banana") {
println!("We have bananas!");
}
println!("All fruits: {:?}", fruit_prices);
}
Try it Yourself ->
The Entry API
The Entry API is a powerful pattern for insert-or-update logic. It lets you avoid checking if a key exists and then performing different actions.
use std::collections::HashMap;
fn main() {
let mut word_count = HashMap::new();
let text = "hello world hello rust hello world";
for word in text.split_whitespace() {
let count = word_count.entry(word).or_insert(0);
*count += 1;
}
println!("Word counts: {:?}", word_count);
}
Try it Yourself ->
Iterating Over HashMaps
You can iterate over key-value pairs, keys, or values. The order isn't guaranteed, but you can sort if needed.
use std::collections::HashMap;
fn main() {
let mut inventory = HashMap::new();
inventory.insert("apples", 50);
inventory.insert("oranges", 30);
inventory.insert("bananas", 25);
for (item, quantity) in &inventory {
println!("{}: {} units", item, quantity);
}
println!("All items: {:?}", inventory.keys().collect::<Vec<_>>());
println!("All quantities: {:?}", inventory.values().collect::<Vec<_>>());
}
Try it Yourself ->
HashMap Ownership
HashMaps follow Rust's ownership rules. For types that implement Copy (like i32), values are copied. For owned types like String, the values are moved into the HashMap.
use std::collections::HashMap;
fn main() {
let name = String::from("Alice");
let age = 25;
let mut person = HashMap::new();
person.insert("name", name);
person.insert("age", age);
println!("{:?}", person);
// name is moved, so this would fail:
// println!("{}", name);
// age is copied, so this works:
println!("Age: {}", age);
}
Try it Yourself ->
Why HashMaps Are Essential
HashMaps are everywhere in real-world programs. They're used for caching, configuration, counting occurrences, and much more. Understanding them is crucial for writing practical Rust code.
The Entry API is particularly powerful because it eliminates common patterns of "check then update" that are error-prone in other languages.