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:
use std::collections::HashMap;HashMap isn't in the prelude (the short list of names every Rust file gets automatically), so you import it.HashMap<String, u32>: every key is aString, every value au32.insert(key, value)adds a pair. Keys are unique: inserting an existing key replaces its value and returns the old one asSome(old)(orNonefor a new key).get(key)returnsOption<&V>, because the key might not be there. A map withStringkeys can be searched with a plain&strlike"apple": you don't have to build aStringjust to look something up.contains_keyanswers yes or no,lencounts the pairs, andremovetakes a pair out and returns its value as anOption.
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
- The one-liner
stock.get("mango").copied().unwrap_or(0)does the same as thatmatch; module 19 coversunwrap_orand friends. get_mutreturnsOption<&mut V>, a mutable reference you can write through with*.stock["kiwi"]reads a value directly, but panics if the key is missing. Use it only when you know the key is there.- This map uses
&strkeys. That's fine when the keys are string literals or text that outlives the map; for text you read at run time, useStringkeys.
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
Assigning through an index:
use std::collections::HashMap; fn main() { let mut stock: HashMap<&str, i32> = HashMap::new(); stock.insert("apple", 3); stock["apple"] = 5; }error[E0594]: cannot assign to data in an index of `HashMap<&str, i32>`Use
insert("apple", 5)to replace, orget_mutto change the value in place.Printing the whole map with
{:?}. It compiles, but the order of the pairs changes from run to run. The next step explains why and what to do instead.
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.