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.

Create a free account to save this page, take notes and track your progress. Sign in

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) returning Option).
  • HashMap<K, V>: key-value lookup (insert, get, contains_key). The entry API 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 like map, filter, enumerate, zip, take, rev build a pipeline; consumers like sum, count, fold, collect, max_by_key run 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

Grouping, totals and top-N with iterators
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 HashMap order. Sort, or use BTreeMap for ordered keys.
  • .iter() vs .into_iter(). into_iter() on a Vec consumes it; use .iter() to keep the collection.
  • get vs indexing. map["key"] panics if missing; map.get("key") returns Option.
  • Sorting floats with sort(). f64 isn't Ord; use sort_by with total_cmp.

Key points

  • Vec for lists, HashMap for lookups, entry().or_insert() for counting and grouping.
  • Closures |args| body capture 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.

Was this page helpful?