AI Instructor Live Labs Included

Iterators, Collections, and Functional Rust

Iterators, closures, and the std collection family. Write adapters, pick the right collection, chain lazily. Course 4 of 12 in the Programming with Rust skill path.

Intermediate
13h 35m
15 Lessons
SMU-RUST-400
Iterators, Collections, and Functional Rust Badge

View badge details

About This Course

Finish Phase 1 of the Programming with Rust path with full fluency on Rust's standard-library data story. You learn the Iterator trait end-to-end, write your own iterator adapters, pick the right collection for a given access pattern (Vec, VecDeque, HashMap, BTreeMap, HashSet, BTreeSet), use closures (Fn, FnMut, FnOnce) correctly, and understand when lazy iterator chains outperform imperative loops and when they don't.By the end of this course you will be able to read any iterator chain in the Rust ecosystem, write your own iterator adapter by implementing the Iterator trait, choose between the six primary std collections based on access shape and performance characteristics, write closures that capture by reference / mutable reference / value correctly, and recognize when .collect() is wasteful vs necessary.Prerequisites: Courses 1, 2, and 3 OR equivalent fluency with ownership, borrows, traits, and generics.

Course Curriculum

15 Lessons
01
AI Lesson
AI Lesson

The Iterator trait, IntoIterator, and consumption patterns

1h 0m

Rust iteration goes under the hood. You learn the Iterator trait as a single-method contract — fn next(&mut self) -> Option<Self::Item> — and watch every other method in the standard library (map, filter, fold, collect, sum, count) emerge from that one primitive. You see IntoIterator as the "turn me into an iterator" bridge that makes for x in v work, and you see the three flavors every container implements: owned move (into_iter()), shared borrow (iter()), and exclusive borrow (iter_mut()). By the end you can pick the right flavor for a given access pattern and read any iterator chain in the ecosystem without stopping on the type signatures.

By the end of this lesson you will be able to describe the Iterator trait + the single required method, explain how adapters derive from it, choose between .iter() / .iter_mut() / .into_iter() for a given situation, and apply the three consumption patterns (for loop, .collect::<T>(), terminal methods) with full awareness of what each one costs.

Prerequisites: Courses 1, 2, and 3 OR equivalent fluency with traits, generics, and ownership.

02
Lab Exercise
Lab Exercise

Iterator basics — iter, iter_mut, into_iter, collect - Lab Exercises

40m 5 Exercises

Practice the three IntoIterator flavors — .iter(), .iter_mut(), .into_iter() — and the main consumption patterns from Lesson 1 on a small Vec<String> of city names. By the end you can look at an iterator-related function signature and pick the right flavor without thinking: shared borrow when you want to keep using the input, exclusive borrow when you want to mutate in place, and owned move when the function consumes the collection to produce its result.

By the end of this lab you will have filled four function stubs that use .iter(), .iter_mut(), .into_iter().max_by_key(...), and .iter().map(...).collect::<Vec<usize>>() respectively, and watched the compiler enforce when each flavor is allowed vs when it leaves the input unusable.

03
AI Lesson
AI Lesson

The adapter zoo — map, filter, take, skip, enumerate, zip, chain, flat_map, fold, reduce

1h 15m

Reach fluency with the Rust iterator adapter zoo — the methods a working Rust developer reaches for daily. You learn the four adapter families (transforms, slicing, combining, terminals), see each in context with concrete worked examples, and practice picking the right one for a given data flow.

By the end of this lesson you will be able to read any iterator chain in the std library and in production Rust crates, choose between map / filter / filter_map for a reshape operation, pick take / skip / take_while to shape iteration boundaries, combine two streams with zip / chain / flat_map, and select the right terminal adapter (fold vs reduce vs sum vs collect) based on the output shape you need.

04
Lab Exercise
Lab Exercise

Refactor loops into adapter chains - Lab Exercises

45m 5 Exercises

Take five short functions that each do their work with a for loop and refactor each one into the equivalent iterator-adapter chain. Across the exercises you move through .filter + .sum, .map + .collect, .zip, .flatten, and .count, and the behavior stays identical the whole way — only the shape of the implementation changes. By the end you can read any typical adapter chain in production Rust and spot the imperative version it replaced. The optional step adds a fold_product to practice .fold as a general-purpose terminal.

05
AI Lesson
AI Lesson

Writing your own iterator by implementing Iterator

1h 0m

Reach beyond the standard-library iterators and build your own. In this lesson you learn to implement the Iterator trait on your own type by writing exactly one method, use the specialization traits (FusedIterator, ExactSizeIterator, DoubleEndedIterator) to unlock faster adapters, and choose between a named-struct iterator (reusable, debuggable) and the stditerfrom_fn closure shortcut (one-off, no name).

By the end of this lesson you will be able to implement Iterator for a custom type by writing the single next method, analyze which specialization traits a given iterator can honor and what adapters each unlocks, and apply stditerfrom_fn for one-off iterators while recognizing when a named struct is the right choice instead.

06
Lab Exercise
Lab Exercise

Implement your own iterators - Lab Exercises

45m 5 Exercises

Build two custom iterators from scratch. The starter ships struct Fibonacci and struct Words<'a> with no Iterator impl — your job is to write them. By the end you understand what the next method actually does, how lifetimes flow through an iterator that borrows from a source, and when to reach for the std::iter::from_fn closure shortcut instead of a named struct.

By the end of this lab you will be able to implement the Iterator trait on your own type, handle a borrowed source with a lifetime parameter on both the struct and the yielded item, add a meaningful size_hint + count override to specialize common cases, and compare the named-struct vs from_fn closure-based approaches for one-off iterators.

07
AI Lesson
AI Lesson

Closures — Fn, FnMut, FnOnce, and capture modes

1h 0m

Closures look simple in Rust until you need to pass one around. This lesson is the full story: what the compiler actually synthesizes behind |x| x + 1, why Fn / FnMut / FnOnce form a hierarchy, how the compiler picks between them based on what the closure body DOES with its captures, and when move is required vs when it's just stylistic. You come out able to read any closure signature in the ecosystem and reason about which bound is appropriate for your own APIs.

By the end of this lesson you can describe how the compiler picks Fn / FnMut / FnOnce for a closure based on its capture usage, choose the right trait bound for a function parameter that accepts a closure, and apply move + impl Fn(...) return types to pass closures across scope boundaries without borrow-checker fights.

Builds on Course 1 Lesson 11 (methods + self receivers) and the ownership story from Course 2. Pure language mechanics — no cloud, no AI, no external crates.

08
Lab Exercise
Lab Exercise

Fn, FnMut, FnOnce — closure drills - Lab Exercises

45m 6 Exercises

Practice the three Fn traits — Fn, FnMut, FnOnce — in six small drills. You pass a plain closure to a Fn-bounded callback, build a FnMut counter that captures a mutable binding, write a FnOnce closure that consumes a String, store a boxed closure as a field on a struct, and return a closure from a factory function using impl Fn. By the end you can look at any closure body and say which Fn trait the compiler will choose, and you can accept, store, and return closures across function boundaries without fighting the type system.

By the end of this lab you will be able to accept a closure as a function parameter with the right Fn bound, build an FnMut closure that captures a mutable binding, consume captures with move + explicit drop, store a Box<dyn Fn(...)> as a struct field, and return impl Fn(...) from a factory function.

09
AI Lesson
AI Lesson

Collections — Vec, VecDeque, HashMap, BTreeMap, HashSet, BTreeSet

1h 0m

Learn when to reach for which std collection. Vec and VecDeque for ordered sequences, HashMap and BTreeMap for key-value lookup, HashSet and BTreeSet for membership tests — plus the choice heuristics that pick the right one in seconds instead of minutes.

By the end of this lesson you will be able to pick between Vec and VecDeque for insertion-pattern constraints, pick between HashMap and BTreeMap on whether sorted iteration matters, pick between the two Set variants on the same basis, and recognize why LinkedList is almost never the right answer in modern Rust.

10
Lab Exercise
Lab Exercise

Pick the right collection — practice drills - Lab Exercises

45m 6 Exercises

Pick the right collection for the shape of the data you have. Across six small scenarios you reach for one of the six primary std collections — VecDeque for a FIFO task queue, BTreeMap for sorted word counts, HashSet for de-duplication, BinaryHeap for a top-N leaderboard, HashMap for fast lookup, and Vec where push/pop at the end is enough. By the end you can scan an access pattern in a few seconds and pick the collection that fits it.

11
AI Lesson
AI Lesson

Lazy vs eager — when .collect() is wasteful vs necessary

1h 0m

Rust iterator chains are famously lazy, and that laziness is what makes them fast. In this lesson you follow the pipeline from the inside — map and filter don't do work, they build a new iterator type that remembers the closure for later; nothing actually runs until a terminal operation (for loop, .sum(), .count(), .collect()) pulls values through. You learn when stacking .collect() between stages wastes an allocation versus when a collect is actually necessary (returning from a function, iterating twice, sorting, indexing). You see the compiler's zero-cost promise — v.iter().map(f).filter(p).sum() produces the same machine code as the hand-rolled for loop — and you measure a chain yourself with stdtimeInstant.

By the end of this lesson you will be able to explain where in an iterator chain the actual work happens, decide for any specific use case whether .collect() to a temporary Vec is wasteful or load-bearing, write chains that avoid intermediate allocations, and run a quick wall-clock measurement on two shapes of the same pipeline to see them perform identically.

Prerequisites: Lessons 1-4 of this course. Reference: The Rust Programming Language 3rd edition, Chapter 13 "Iterators and Closures"; Rust for Rustaceans Chapter 10 for the zero-cost deep dive.

12
Lab Exercise
Lab Exercise

Measure lazy vs eager — iterator benchmarks - Lab Exercises

45m 5 Exercises

Iterator chains in Rust are lazy — nothing happens until a terminal pulls values through. You take a wasteful pipeline that calls .collect() into a throwaway Vec and compare it against the single-pass equivalent, measuring the actual time difference with std::time::Instant at four different input sizes. Then you look at the cases where .collect() is actually load-bearing (you need the length AND the Vec, you need to index, or you need the collected slice to outlive the source iterator).

By the end of this lab you can look at an iterator chain and tell at a glance whether a .collect() is wasteful boilerplate or doing real work.

13
AI Lesson
AI Lesson

Designing iterator-friendly APIs

45m

Reach the final skill in the Phase 1 arc of the Programming with Rust path — writing functions that accept and return iterators cleanly. You learn to prefer impl IntoIterator<Item = T> over &Vec<T> so callers can pass arrays, ranges, iterator chains, and anything else that yields values, and you learn when to return impl Iterator<Item = T> for lazy pipelines versus Vec<T> when the caller legitimately needs a container.

By the end of this lesson you will be able to pick between IntoIterator and Iterator for a function parameter, annotate lifetimes correctly on impl Iterator return types, apply the newtype iterator pattern to hide internal iterator types across API boundaries, and explain to a code reviewer why returning Vec<T> is the right call for a specific use case.

This lesson is intentionally shorter than the earlier Course 4 lessons — API design is a small set of decisions applied carefully, not a large concept surface.

14
Lab Exercise
Lab Exercise

Design iterator-friendly APIs - Lab Exercises

40m 5 Exercises

Three small APIs, three refactors. You start with a total function that only accepts &Vec<i32>, a repeated function that eagerly allocates a Vec<String>, and a Fibonacci struct with fields and a new() method but no Iterator impl. Across the exercises you widen total to accept any IntoIterator, make repeated return a lazy impl Iterator<Item = String>, wrap Fibonacci in the newtype iterator pattern, and expose a factory function. By the end you can look at any function signature in a Rust API and reason about whether it is as flexible as it should be.

15
Lab Exercise
Lab Exercise

Capstone — Build a streaming log analyzer with iterator chains - Lab Exercises

1h 30m 6 Exercises

Capstone of Course 4 — Programming with Rust Phase 1. You take ~50 lines of NGINX-style access log, parse each into a LogEntry, filter out failed requests, group latencies by request path, compute per-path stats (count, average, p95), and print a top-N report sorted by request count. Every stage is an iterator chain; Vec allocations happen only where the pipeline needs to pivot (sorting, HashMap collection, or final output).

By the end of this lab you will have exercised every piece of Course 4 — iterator transforms + terminals, filter_map on parse results, HashMap grouping with fold-style accumulation, lazy versus eager evaluation choices, and an API that takes an owned Vec and returns a lazy iterator of stats. You will also have a reusable mental model for the "parse → filter → group → aggregate → report" shape that shows up in almost every production data-processing CLI.

No external crates. Pure std. Rust 1.99.1, edition 2024.

This course includes:

  • 24/7 AI Instructor Support
  • Live Lab Environments
  • 8 Hands-on Lessons
  • Completion Badge
Iterators, Collections, and Functional Rust Badge

Earn Your Badge

Complete all lessons to unlock the Iterators, Collections, and Functional Rust achievement badge.

Skill Level Intermediate
Total Duration 13h 35m
Iterators, Collections, and Functional Rust Badge
Achievement Badge

Iterators, Collections, and Functional Rust

Awarded for completing SMU-RUST-400. Demonstrates fluency with the Iterator trait and the full adapter zoo (map, filter, fold, take_while, chain, zip, flat_map), the six primary std collections, closures (Fn / FnMut / FnOnce), and lazy-vs-eager evaluation choices.

Course Iterators, Collections, and Functional Rust
Criteria Complete all lessons with assessment scores of 80% or above and submit the final capstone (build a small streaming data-processing pipeline using iterator chains).

Skills You'll Earn

Iterator trait Custom iterator adapters map / filter / fold / flat_map Collections (Vec, VecDeque, HashMap, BTreeMap, HashSet, BTreeSet) Closures (Fn, FnMut, FnOnce) Lazy vs eager evaluation collect idioms

Complete all lessons in this course to earn this badge