Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

Rust HashMap

A vector finds items by position: the first, the second, the tenth. Often you want to find things by a name instead. What is Asha's phone number? How many times does the word "the" appear? What is the price of a mango? A HashMap stores pairs of a key and a value, and finds the value for a key very quickly, like a dictionary where you look up a word to find its meaning.

HashMap is not loaded automatically, so you bring it in with use:

use std::collections::HashMap;

fn main() {
    let mut prices = HashMap::new();
    prices.insert("mango", 60);
    prices.insert("banana", 40);
    prices.insert("apple", 120);

    println!("A mango costs {}", prices["mango"]);
    println!("We sell {} fruits", prices.len());
}
Output
A mango costs 60
We sell 3 fruits

Every key appears only once. Inserting a key that is already there replaces its old value.

Looking up safely with get

prices["kiwi"] would panic if there is no kiwi. get returns an Option instead, so you can deal with a missing key calmly:

use std::collections::HashMap;

fn main() {
    let mut capitals = HashMap::new();
    capitals.insert("India", "New Delhi");
    capitals.insert("Japan", "Tokyo");

    for country in ["Japan", "Peru"] {
        match capitals.get(country) {
            Some(city) => println!("The capital of {} is {}", country, city),
            None => println!("I do not know the capital of {} yet", country),
        }
    }
    println!("Knows India? {}", capitals.contains_key("India"));
}
Output
The capital of Japan is Tokyo
I do not know the capital of Peru yet
Knows India? true

Counting with entry

The classic HashMap job is counting. entry(key).or_insert(0) finds the counter for a key, creating it at 0 if it is new, and gives you a mutable reference to it. The * reaches through the reference to add one:

use std::collections::HashMap;

fn main() {
    let text = "red blue red green blue red";
    let mut counts: HashMap<&str, u32> = HashMap::new();
    for colour in text.split_whitespace() {
        *counts.entry(colour).or_insert(0) += 1;
    }

    let mut colours: Vec<_> = counts.iter().collect();
    colours.sort();
    for (colour, n) in colours {
        println!("{}: {}", colour, n);
    }
}
Output
blue: 2
green: 1
red: 3

Why sort before printing?

A HashMap does not keep its items in any particular order. Looping over it directly gives an order that can change from one run to the next. That is fine for looking things up, but confusing when you print. So here we collect the pairs into a vector and sort it first. If you always want keys in order, BTreeMap from the same module works just like HashMap but keeps its keys sorted.

Removing and updating

remove(key) takes a pair out and gives back the value as an Option. To change a value in place, use get_mut or entry:

use std::collections::HashMap;

fn main() {
    let mut stock = HashMap::new();
    stock.insert("pens", 10);
    stock.insert("books", 4);
    if let Some(n) = stock.get_mut("pens") {
        *n -= 3;
    }
    let gone = stock.remove("books");
    println!("pens left: {}, removed books: {:?}", stock["pens"], gone);
}
Output
pens left: 7, removed books: Some(4)

Practice exercises

  1. Make a HashMap of three friends and their birthdays, and look one of them up.
  2. Count how many times each letter appears in the word "banana", and print the counts in alphabetical order.
  3. Build a tiny phone book: ask for a name and print the number, or a friendly message if it is missing.