Collections & Iterators
Chapter 2's arrays were fixed-size. This final chapter of Course 1 covers Rust's growable collections — and the iterator adapters that process them in a style that will feel immediately familiar coming from JavaScript.
Vec<T>: A Growable Array
Vec<T> can grow and shrink at runtime, backed by a heap allocation — genuinely similar to Go's own slice type, which is Go's own answer to "an array that can grow." This is a real similarity worth noting, not just another contrast.
HashMap<K, V>: Key-Value Storage
get returns Option<&V> — Chapter 6's Option making a direct return appearance. Go's built-in map[string]int is more tightly woven into the language's own syntax, and its lookup returns a (value, ok bool) pair — both languages are explicit about "this key might not exist," just expressed through different mechanisms (Go's two-value return vs. Rust's Option).
Iterator Adapters: map, filter, collect
Rust's iterator methods will feel immediately familiar coming from JavaScript's own array methods — the naming and behavior are genuinely close.
.iter() creates an iterator of references (borrowing, Chapter 4) without consuming numbers. .filter() and .map() are lazy — nothing runs until .collect() actually consumes the chain and builds a new Vec.
| JavaScript | Rust | |
|---|---|---|
| Transform each element | array.map(x => ...) | .iter().map(|x| ...) |
| Keep matching elements | array.filter(x => ...) | .iter().filter(|x| ...) |
| Execution | Eager — runs immediately | Lazy — runs only when collected/consumed |
Ownership Implications of Iterating
Rust has three distinct ways to iterate, and choosing the wrong one is a genuinely common point of confusion:
| Method | Yields | Collection Usable After? |
|---|---|---|
.iter() | &T (borrowed references) | Yes |
.iter_mut() | &mut T (mutable references) | Yes |
.into_iter() / bare for x in vec | T (owned values) | No — consumed/moved (Ch.3) |
Neither Go nor JavaScript has this distinction at all — their iteration never "consumes" the underlying collection the way into_iter() does.
Vec<T> and HashMap<K,V> are both heap-allocated — meaning Chapter 3's move semantics apply to them exactly as they did to String. Assigning one Vec to another variable moves it, not copies it; .clone() is still the explicit way to get a real independent duplicate. Every chapter in this course has been building toward being able to read code like this one's filter/map/collect chain and immediately know exactly what's borrowed, what's owned, and what's been moved.
collect() is generic over what it builds — it needs to know the target type from somewhere, either an explicit variable type annotation (let x: Vec<i32> = ...) or the "turbofish" syntax (.collect::<Vec<i32>>()). Omitting both produces a real, common compiler error demanding a type annotation — not a bug in the code's logic, just collect() genuinely being unable to infer what to build without more information.
Coding Challenges
Create a Vec of five integers. Using an iterator chain (.iter(), .filter(), .map(), .collect()), produce a new Vec containing only the odd numbers, each squared.
📄 View solutionCreate a HashMap mapping three product names (String) to their prices (f64). Look up one product that exists and one that doesn't, handling both cases with match on the Option returned by get.
📄 View solutionExplain the difference between iterating with .iter(), .iter_mut(), and into_iter() (or a bare for x in vec), specifically in terms of what happens to the original collection afterward in each case.
📄 View solutionChapter 8 Quick Reference
- Vec<T> — a growable, heap-backed array;
vec![...]macro orVec::new()+.push() - HashMap<K, V> — key-value storage;
.get()returnsOption<&V> - .iter().map(...).filter(...).collect() — a lazy, chainable iterator pipeline, close to JavaScript's array methods
- .iter() / .iter_mut() — borrow (immutably/mutably); the collection remains usable afterward
- .into_iter() / bare
for x in vec— consumes the collection; it's moved and unusable afterward - collect() needs a target type — via variable annotation or the turbofish
::<Type>() - Vec and HashMap are heap-allocated — Chapter 3's move semantics apply to both directly
★ Rust Fundamentals Complete — 8 / 8 chapters
From cargo and the toolchain through variables, ownership, borrowing, structs, enums, error handling, and finally collections and iterators — you now have the complete foundation Rust's whole design is built on. Next up: Rust Intermediate, covering lifetimes, traits, generics, smart pointers, concurrency, modules, and testing.