Site

HashMap<K,V> — Rust Hash Map

Tutorial S3.0  •  Rust / Learn / StdLib

S3.0 What This Teaches

HashMap<K, V> stores key-value pairs with O(1) average lookup. This tutorial covers:

S3.1 Construction and Insertion

HashMap is in std::collections, so bring it into scope with use:
use std::collections::HashMap;

let mut scores: HashMap<String, u32> = HashMap::new();
scores.insert(String::from("Alice"), 90);
scores.insert(String::from("Bob"),   75);
insert returns Option<V> - Some(old_value) if the key already existed, None if it was new. A second insert with the same key overwrites the value.

S3.2 get and contains_key

get returns Option<&V> - a reference to the value, or None if the key is absent:
if let Some(s) = scores.get("Alice") {
    println!("Alice: {s}");
}
println!("{:?}", scores.get("Dave"));       // None
println!("{}", scores.contains_key("Bob")); // true
Use scores["Alice"] for direct access when you are certain the key exists - it panics if it does not.

S3.3 remove

scores.remove("Bob");
remove returns Option<V> - the removed value, or None if the key was absent.

S3.4 The Entry API

The entry API handles "insert if absent, otherwise leave alone" in one operation - no double lookup:
scores.entry(String::from("Dave")).or_insert(70);  // adds Dave: 70
scores.entry(String::from("Alice")).or_insert(0);  // Alice exists; unchanged
or_insert returns a mutable reference to the value (existing or newly inserted). Use it directly to modify the value in place:
let alice = scores.entry(String::from("Alice")).or_insert(0);
*alice += 5;
This pattern is the idiomatic way to build a frequency counter:
let text = "the quick brown fox jumps over the lazy dog the fox";
let mut freq: HashMap<&str, u32> = HashMap::new();
for word in text.split_whitespace() {
    *freq.entry(word).or_insert(0) += 1;
}

S3.5 Iteration

HashMap does not guarantee order. Sort before printing when you need stable output:
let mut pairs: Vec<(&String, &u32)> = scores.iter().collect();
pairs.sort_by_key(|(k, _)| k.as_str());
for (name, score) in pairs {
    println!("{name}: {score}");
}
Other iteration forms: .keys(), .values(), .into_iter() (consumes the map and yields owned key-value pairs).

S3.6 Example - All Together

// HashMap - demonstrates construction, lookup, the entry API, and iteration.

use std::collections::HashMap;

fn main() {
    // --- construction ---
    println!("--- construction ---");
    let mut scores: HashMap<String, u32> = HashMap::new();
    scores.insert(String::from("Alice"), 90);
    scores.insert(String::from("Bob"),   75);
    scores.insert(String::from("Carol"), 88);
    println!("{scores:?}");

    // --- get: returns Option<&V> ---
    println!("--- get ---");
    if let Some(s) = scores.get("Alice") {
        println!("Alice: {s}");
    }
    println!("Dave: {:?}", scores.get("Dave"));

    // --- contains_key ---
    println!("--- contains_key ---");
    println!("has Bob: {}", scores.contains_key("Bob"));

    // --- remove ---
    println!("--- remove ---");
    scores.remove("Bob");
    println!("after remove Bob: {} entries", scores.len());

    // --- entry: insert only if absent ---
    println!("--- entry or_insert ---");
    scores.entry(String::from("Dave")).or_insert(70);
    scores.entry(String::from("Alice")).or_insert(0);  // Alice exists; unchanged
    println!("Dave: {}, Alice: {}", scores["Dave"], scores["Alice"]);

    // --- entry: mutate existing value ---
    println!("--- entry modify ---");
    let alice = scores.entry(String::from("Alice")).or_insert(0);
    *alice += 5;
    println!("Alice after +5: {}", scores["Alice"]);

    // --- sorted iteration ---
    println!("--- iteration ---");
    let mut pairs: Vec<(&String, &u32)> = scores.iter().collect();
    pairs.sort_by_key(|(k, _)| k.as_str());
    for (name, score) in pairs {
        println!("{name}: {score}");
    }

    // --- word frequency (classic entry pattern) ---
    println!("--- word count ---");
    let text = "the quick brown fox jumps over the lazy dog the fox";
    let mut freq: HashMap<&str, u32> = HashMap::new();
    for word in text.split_whitespace() {
        *freq.entry(word).or_insert(0) += 1;
    }
    let mut fv: Vec<(&&str, &u32)> = freq.iter().collect();
    fv.sort_by_key(|(w, _)| **w);
    for (word, count) in fv {
        println!("  {word}: {count}");
    }
}
Expected output (construction line order varies - HashMap is unordered):
--- construction ---
{"Bob": 75, "Carol": 88, "Alice": 90}
--- get ---
Alice: 90
Dave: None
--- contains_key ---
has Bob: true
--- remove ---
after remove Bob: 2 entries
--- entry or_insert ---
Dave: 70, Alice: 90
--- entry modify ---
Alice after +5: 95
--- iteration ---
Alice: 95
Carol: 88
Dave: 70
--- word count ---
  brown: 1
  dog: 1
  fox: 2
  jumps: 1
  lazy: 1
  over: 1
  quick: 1
  the: 3

S3.7 Exercise

Exercise
  • Build a HashMap<&str, Vec<i32>> that groups numbers by a label. Insert "odd" -> [1,3,5] and "even" -> [2,4,6]. Print both groups.
  • Write a function char_frequency(s: &str) -> HashMap<char, usize> that counts how often each character appears. Test it on "mississippi" and print results in alphabetical order.
  • Given two HashMap<String, u32> maps, write a function that merges the second into the first, summing values for keys that appear in both.

S3.8 Common Mistakes

Indexing with a key that may not exist

println!("{}", scores["Dave"]);   // panics if Dave is absent
Use scores.get("Dave") and handle the Option.

Unnecessary allocation on lookup

scores.insert(String::from("Alice"), 90);
scores.get(&"Alice".to_string());   // allocates needlessly
scores.get("Alice");                // works - &str coerces correctly
HashMap accepts any type that implements Borrow<K> for lookup. &str works directly for String keys - no .to_string() needed.

Assuming iteration order

for (k, v) in &scores {
    println!("{k}: {v}");   // order is non-deterministic across runs
}
HashMap uses a random hash seed. Collect into a Vec and sort before printing when consistent output is required.

S3.9 Key Terms

TermMeaning
HashMap<K, V>Hash table mapping keys of type K to values of type V
insertAdds or overwrites a key-value pair; returns the old value as Option<V>
getReturns Option<&V> for a given key; does not panic
removeRemoves a key and returns its value as Option<V>
entry APIentry(key).or_insert(val) - insert-if-absent with one lookup
or_insertReturns &mut V to the existing or newly inserted value
contains_keyReturns bool indicating whether the key is present
BorrowTrait that lets String keys be looked up via &str references