RustArena

Step 1 of 6

Looking things up with HashMap

A HashMap<K, V> stores key-value pairs: you file a value under a key, then ask for the value by its key. A price list (item to price), a phone book (name to number), an inventory (product to quantity). To find "kiwi" in a vector you'd check every element; a HashMap computes a number from the key, its hash, and uses it to jump almost straight to the right slot. A lookup takes about the same time with ten entries or ten million.

use std::collections::HashMap;

fn main() {
    let mut stock: HashMap<String, u32> = HashMap::new();
    stock.insert(String::from("apple"), 12);
    stock.insert(String::from("pear"), 0);
    stock.insert(String::from("kiwi"), 7);

    println!("{} kinds", stock.len());
    println!("apples: {:?}", stock.get("apple"));
    println!("mangos: {:?}", stock.get("mango"));
    println!("kiwi listed? {}", stock.contains_key("kiwi"));

    let old = stock.insert(String::from("apple"), 20);
    println!("apples were {:?}, now {:?}", old, stock.get("apple"));

    let removed = stock.remove("pear");
    println!("removed {:?}, {} kinds left", removed, stock.len());
}
3 kinds
apples: Some(12)
mangos: None
kiwi listed? true
apples were Some(12), now Some(20)
removed Some(0), 2 kinds left

Piece by piece:

Getting values out and changing them

get hands you a reference inside an Option. To get a plain number, with a default for missing keys, match on it; the pattern Some(&n) matches the reference and copies the number out of it (the same & trick as retain(|&r| ...) in the vectors module):

use std::collections::HashMap;

fn main() {
    let mut stock: HashMap<&str, u32> = HashMap::new();
    stock.insert("kiwi", 7);

    let mangos = match stock.get("mango") {
        Some(&n) => n,
        None => 0,
    };
    println!("mangos: {mangos}");

    if let Some(qty) = stock.get_mut("kiwi") {
        *qty -= 1;
    }
    println!("kiwis: {}", stock["kiwi"]);
}
mangos: 0
kiwis: 6

insert takes ownership of the key and the value: after stock.insert(name, 3) with a String name, the map owns it and name can't be used any more.

Common mistakes

Your task

Write a price checker. Create a HashMap<&str, u32> and insert these prices in cents: apple 125, bread 349, milk 99, eggs 450. Then, for each input line (an item name), look it up with get and print ITEM: PRICE cents, or ITEM: not sold here if the item isn't in the map.

Challenge 2

Write fn restock(stock: &mut HashMap<String, u32>, item: &str, amount: u32): if the item is already in the map, add amount to its quantity; if not, insert it with amount. Use get_mut and insert (the next steps show a shortcut).

Challenge 3

Write fn sell(stock: &mut HashMap<String, u32>, item: &str, qty: u32) -> bool. If the item is missing or there aren't at least qty of it, return false and change nothing. Otherwise subtract qty and return true; if that leaves 0, remove the item from the map entirely.

Next: Iteration order and BTreeMap