Rust Fundamentals: Ownership, Errors and Fearless Concurrency · Errors, Collections, Traits and Concurrency
Vec, HashMap, Iterators and Closures
Store data in Vec and HashMap, and process it with iterator chains (map, filter, fold, collect) and closures. Iterators are lazy and compile down to loops as fast as hand-written ones.
What you'll build
A sales report: group orders by customer in a HashMap, total them with iterator chains, find the top customers, and count words with the entry API.
The idea
Vec<T>: a growable list (vec![1, 2, 3],push,pop,len, indexing,.get(i)returningOption).HashMap<K, V>: key-value lookup (insert,get,contains_key). TheentryAPI inserts or updates in one step:*counts.entry(word).or_insert(0) += 1;.- Closures are anonymous functions that can capture variables:
|x| x * 2,|o: &Order| o.total > 100. - Iterators:
.iter()(borrows items),.iter_mut()(mutable borrows),.into_iter()(takes ownership). Adapters likemap,filter,enumerate,zip,take,revbuild a pipeline; consumers likesum,count,fold,collect,max_by_keyrun it.
Iterators are lazy: nothing happens until a consumer runs the chain. The compiler turns these chains into tight loops, so they're both readable and fast ("zero-cost abstractions").
The code
use std::collections::HashMap;
struct Order {
customer: &'static str,
total: f64,
}
fn main() {
let orders = vec![
Order { customer: "ana", total: 120.0 },
Order { customer: "ben", total: 35.5 },
Order { customer: "ana", total: 80.0 },
Order { customer: "cleo", total: 210.0 },
Order { customer: "ben", total: 64.5 },
];
let revenue: f64 = orders.iter().map(|o| o.total).sum();
let big = orders.iter().filter(|o| o.total >= 100.0).count();
println!("revenue {revenue:.2}, {big} big orders");
let mut by_customer: HashMap<&str, f64> = HashMap::new();
for o in &orders {
*by_customer.entry(o.customer).or_insert(0.0) += o.total;
}
let mut ranking: Vec<(&str, f64)> = by_customer.into_iter().collect();
ranking.sort_by(|a, b| b.1.total_cmp(&a.1)); // biggest first
for (i, (name, total)) in ranking.iter().take(2).enumerate() {
println!("#{} {name}: {total:.2}", i + 1);
}
let threshold = 50.0; // closures capture variables from their surroundings
let names: Vec<String> = orders
.iter()
.filter(|o| o.total > threshold)
.map(|o| o.customer.to_uppercase())
.collect();
println!("over {threshold}: {}", names.join(", "));
let text = "the cat and the hat and the bat";
let mut counts: HashMap<&str, usize> = HashMap::new();
for word in text.split_whitespace() {
*counts.entry(word).or_insert(0) += 1;
}
let mut common: Vec<_> = counts.into_iter().collect();
common.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(b.0)));
println!("most common: {:?}", &common[..3]);
}
Output
revenue 510.00, 2 big orders
#1 cleo: 210.00
#2 ana: 200.00
over 50: ANA, ANA, CLEO, BEN
most common: [("the", 3), ("and", 2), ("bat", 1)]Run this on your computer — see “Get Started” for setup.
HashMap iteration order is random, which is why the program sorts before printing. Never rely on map order in output or tests.
Common mistakes
- Expecting iterator chains to run without a consumer.
orders.iter().map(...)alone does nothing (and the compiler warns). - Relying on
HashMaporder. Sort, or useBTreeMapfor ordered keys. .iter()vs.into_iter().into_iter()on aVecconsumes it; use.iter()to keep the collection.getvs indexing.map["key"]panics if missing;map.get("key")returnsOption.- Sorting floats with
sort().f64isn'tOrd; usesort_bywithtotal_cmp.
Key points
Vecfor lists,HashMapfor lookups,entry().or_insert()for counting and grouping.- Closures
|args| bodycapture their environment. - Iterator adapters are lazy; consumers (
sum,collect,count) run them. - Zero-cost: iterator chains compile to efficient loops.
Further reading: Common collections, Iterators and closures.
Practice questions
What does it mean that Rust iterators are lazy?
Answer
Adapter methods like map and filter only build a description of the work; nothing runs until a consumer such as collect, sum, count or a for loop pulls items through the chain.
How do you count occurrences of words with a HashMap in Rust?
Answer
Loop over the words and use the entry API: *counts.entry(word).or_insert(0) += 1; which inserts 0 for new keys and increments in place.
Open in the app to mark lessons complete, take notes and track your progress.