Storing Keys with Associated Values in Hash Maps
HashMap<K, V>
stores key-value pairs using a hashing function. Keys must implement Eq
and Hash
traits.
Creating Hash Maps
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50);
Hash maps are not included in the prelude, require explicit use
. No built-in macro like vec!
.
Accessing Values
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); // Returns Option<&V> let score = scores.get("Blue").copied().unwrap_or(0); // Iterate over key-value pairs for (key, value) in &scores { println!("{key}: {value}"); }
Ownership Rules
use std::collections::HashMap; let field_name = String::from("Favorite color"); let field_value = String::from("Blue"); let mut map = HashMap::new(); map.insert(field_name, field_value); // field_name and field_value are moved, no longer valid // For Copy types like i32, values are copied // For references, values must live as long as the hash map
Updating Hash Maps
Overwriting values:
scores.insert(String::from("Blue"), 25); // Replaces existing value
Insert only if key doesn’t exist:
scores.entry(String::from("Yellow")).or_insert(50); scores.entry(String::from("Blue")).or_insert(50); // Won't insert, Blue exists
Update based on old value:
let text = "hello world wonderful world"; let mut map = HashMap::new(); for word in text.split_whitespace() { let count = map.entry(word).or_insert(0); *count += 1; // Dereference to modify the value }
The entry
method returns an Entry
enum that represents a value that might or might not exist. or_insert
returns a mutable reference to the value.
Performance Notes
- Default hasher is SipHash (cryptographically secure, DoS resistant)
- For performance-critical code, consider alternative hashers via
BuildHasher
trait - All keys must be same type, all values must be same type
Common Patterns
// Check if key exists if scores.contains_key("Blue") { /* ... */ } // Remove key-value pair scores.remove("Blue"); // Get mutable reference to value if let Some(score) = scores.get_mut("Blue") { *score += 10; } // Merge two hash maps for (k, v) in other_map { scores.insert(k, v); }
Hash maps provide O(1) average-case performance for insertions, deletions, and lookups, making them suitable for caching, counting, and indexing operations common in web applications.
Summary
Vectors, strings, and hash maps will provide a large amount of functionality necessary in programs when you need to store, access, and modify data. Here are some exercises you should now be equipped to solve:
- Given a list of integers, use a vector and return the median (when sorted, the value in the middle position) and mode (the value that occurs most often; a hash map will be helpful here) of the list.
- Convert strings to pig latin. The first consonant of each word is moved to the end of the word and ay is added, so first becomes irst-fay. Words that start with a vowel have hay added to the end instead (apple becomes apple-hay). Keep in mind the details about UTF-8 encoding!
- Using a hash map and vectors, create a text interface to allow a user to add employee names to a department in a company; for example, “Add Sally to Engineering” or “Add Amir to Sales.” Then let the user retrieve a list of all people in a department or all people in the company by department, sorted alphabetically.
The standard library API documentation describes methods that vectors, strings, and hash maps have that will be helpful for these exercises!
We’re getting into more complex programs in which operations can fail, so it’s a perfect time to discuss error handling. We’ll do that next!