What is a HashMap in Rust?
A HashMap is part of the std: :collections module and is represented as HashMap<K, V>, where:
- K is the type of keys.
- V is the type of values.
Each key must be unique, and values are accessed using their corresponding keys.
Why Use a HashMap?
- Key-Value Pair Storage: Ideal for mapping data relationships, such as storing a student’s name and their grades.
- Fast Access: Lookups and updates are fast due to the underlying hash function.
- Flexible Keys and Values: Supports custom types for keys and values.
Creating a HashMap
To use HashMaps, you need to include the std: :collections module.
1. Using HashMap: :new
You can create an empty HashMap and add entries later.
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
scores.insert("Bob", 80);
println!("{:?}", scores); // Output: {"Alice": 90, "Bob": 80}
}
2. Using the from Method
You can initialize a HashMap with predefined values.
use std::collections::HashMap;
fn main() {
let scores: HashMap<&str, i32> = [("Alice", 90), ("Bob", 80)].into_iter().collect();
println!("{:?}", scores); // Output: {"Alice": 90, "Bob": 80}
}
Accessing Values in a HashMap
To access a value, use the key. The .get() method returns an Option<&V>.
Example: Accessing a Value
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
match scores.get("Alice") {
Some(&score) => println!("Alice's score is {}", score), // Output: Alice's score is 90
None => println!("No score found for Alice"),
}
}
Direct Indexing
You can use square brackets, but this will panic if the key is not found.
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
println!("{}", scores["Alice"]); // Output: 90
}
Modifying a HashMap
You can modify values or add/remove entries in a HashMap.
1. Adding or Updating a Value
Use .insert() to add a new key-value pair or update an existing value.
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
scores.insert("Alice", 95); // Updates the value
println!("{:?}", scores); // Output: {"Alice": 95}
}
2. Updating Only if Key Exists
Use .entry() with .or_insert() to modify values conditionally.
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
scores.entry("Alice").or_insert(95); // Won't update as "Alice" exists
scores.entry("Bob").or_insert(85); // Adds "Bob"
println!("{:?}", scores); // Output: {"Alice": 90, "Bob": 85}
}
3. Removing a Key
Use .remove() to delete a key-value pair.
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
scores.remove("Alice");
println!("{:?}", scores); // Output: {}
}
Iterating Over a HashMap
You can loop through a HashMap using .iter().
Example: Iterating Over Key-Value Pairs
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
scores.insert("Bob", 80);
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// Output:
// Alice: 90
// Bob: 80
}
Ownership and HashMaps
Keys and values are moved into the HashMap unless they implement the Copy
trait. To retain ownership, use references.
Example: Using References
use std::collections::HashMap;
fn main() {
let name = String::from("Alice");
let mut scores = HashMap::new();
scores.insert(&name, 90); // Use reference to avoid moving `name`
println!("{:?}", scores); // Output: {"Alice": 90}
}
HashMap Operations
1. Checking Key Existence
Use .contains_key() to verify if a key exists.
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
println!("{}", scores.contains_key("Alice")); // Output: true
}
2. Clearing a HashMap
Use .clear() to remove all entries.
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
scores.clear();
println!("{:?}", scores); // Output: {}
}
3. Merging HashMaps
Combine two HashMaps using .extend().
use std::collections::HashMap;
fn main() {
let mut scores1 = HashMap::new();
scores1.insert("Alice", 90);
let mut scores2 = HashMap::new();
scores2.insert("Bob", 80);
scores1.extend(scores2);
println!("{:?}", scores1); // Output: {"Alice": 90, "Bob": 80}
}
Performance Considerations
- Hash Function: The default hash function is designed for speed and security but can be replaced with a custom one.
- Memory Usage: HashMaps consume more memory compared to arrays or vectors due to internal hashing and resizing mechanisms.
- Pre-Allocation: Use .with_capacity() to initialize a HashMap with a predefined size.