Iterators¶
One line: An iterator is a sequence that computes nothing until somebody asks — one next method, seventy-five more that arrive free, and three different doors onto every collection depending on whether you mean to read it, change it, or take it apart.
for row in &rows is on half the pages in this library, and nothing before this section said what the & was choosing, why the same loop without it consumes rows, or why a chain of map and filter allocates nothing in between.
The property everything else follows from is laziness: map and filter build a value describing the work, and a consumer like collect or find is what runs it — element at a time, all the way through the chain, stopping the moment it has its answer. That is why an endless sequence is usable, why take(2) can stop a source six elements long after two, and why the fluent style costs nothing that a hand-written loop would not.
| Lesson | Level | What it teaches |
|---|---|---|
| Iterators are lazy | 201 | Adapters build a plan and consumers run it, counted: 6 closure calls for collect, 1 for find, 0 for a chain nobody consumed — plus the interleaving proof that a chain is one pass, not one per adapter |
iter, iter_mut, into_iter |
101 → 201 | The three doors onto a collection, which one a for loop picked for you, the array into_iter that changed meaning in edition 2021, and why the clone you added to make it compile was probably the wrong door |
Implementing Iterator |
201 → 301 | One method and seventy-five arrive free; what does not (rev, len, size_hint); and why a collection must never be an iterator |
fold and reduce |
201 | The consumer the others are made of — sum really is fold(0, +) in std — plus try_fold, and the .clone() inside a fold that makes it quadratic |
Collect the iterator into a Vec |
101 → 201 | Materialize, or just walk it: what the Vec buys, the six questions that need none of it, the pieces that turn out to be slices of the original — and why a Vec<&str> can never outlive its string |
collect and FromIterator |
201 | The call with no behaviour of its own: the target type decides. Including Result<Vec<_>, _>, which flips a sequence of failures into one, and keeps only the first |
| Adapters by job | 201 | Which of the twenty to reach for, and the four traps — take_while is not filter, and flat_map over a Result deletes your errors |
zip and enumerate |
101 → 201 | Walking two sequences as one: the (i, (a, b)) pattern, and two silent failures — zip ending at the shorter side with no warning, and enumerate numbering what reaches it rather than the source |
| Returning an iterator | 201 → 301 | impl Iterator for the chain you cannot name, the lifetime edition 2024 now captures for you, and the second branch that forces a box |
DoubleEndedIterator and ExactSizeIterator |
201 → 301 | Where rev() and len() actually come from, what each promises, and why .enumerate().rev() and .rev().enumerate() number the same rows differently |
When a for loop beats a chain |
201 | Four jobs the fluent style loses — an error that names its row, a growing work list, a break out of two levels, three answers in one pass — and why speed is not one of them |
Iterator versus Stream |
301 | The same trait with "not yet" added to the answer, polled by hand with a nine-line executor; and why an Iterator inside async code never yields |
The first three are the spine — read them in that order, since laziness explains what the other two are describing and the three doors have to be solid before you write the doors for a type of your own. After that the section stops being a sequence: fold and collect are the two consumers worth knowing properly, adapters by job is a lookup table with zip and enumerate expanded out of it, and the last three are each answers to a question you will not have until you hit it.
They all take closures¶
23_Closures/ is the other half, and the two sections were one section until this one outgrew it. Every adapter here takes a closure, and the bound in its signature is a promise about what it will do with yours: map takes an FnMut because it runs per item and may carry state, while a fallback that runs at most once can take an FnOnce and let you move an owned value out of it. If a chain is refusing your closure, that page is usually the answer.
Iterator pages that live elsewhere¶
Iterators turn up long before a section about them, so their lessons stay where a reader will meet them:
Optionis a one-item collection — every adapter on this page, over a sequence of length 0 or 1- Walking a string —
chars,bytes,char_indicesand the split family: the three-door question for text, where the answer is different str::splitand its neighbours — one page per method, most of them returning an iterator- A generic recursive type — the linked list, which is where writing
nextby hand stops being optional - What a trait is — required versus provided methods, the mechanism behind "write one, get seventy-five"
- Endless iteration — the loop that never ends because "no more input" is not an error (a stub)
Not yet written¶
The remaining gaps, listed rather than stubbed so they are visible: itertools, the crate everybody adds for chunk_by, sorted, dedup_by and Either — and where its adapters differ from the std ones they resemble; iterating a HashMap and why the order is deliberately not the insertion order; Extend and Iterator::by_ref, the two mechanisms that let a chain be consumed in stages; parallel iterators (rayon's par_iter), which is a one-word change with a real contract behind it; and writing an iterator over a tree, where next has to hold an explicit stack because the recursion cannot.
Po polsku¶
Iterator po polsku to po prostu iterator — słowo jest to samo. Zmienia się co innego: dwa angielskie terminy, na których stoi cały ten rozdział, nie mają w polszczyźnie utrwalonego odpowiednika, więc lepiej ich nie tłumaczyć. Adapter (adaptor: map, filter, take) tylko opisuje pracę, a konsument (consumer: collect, find, sum, sama pętla for) jest tym, co ją wykonuje. Z tego podziału wynika cała reszta: łańcuch, którego nikt nie skonsumował, nie wywoła twojego domknięcia (closure) ani razu i nic nie zaalokuje, a take(2) potrafi zatrzymać sześcioelementowe źródło po dwóch elementach — bo przez łańcuch płynie po jednym elemencie przez wszystkie ogniwa, a nie cała kolekcja przez jedno ogniwo naraz.
Drugie pytanie, z którym się tu przychodzi, brzmi: co właściwie wybiera & w for row in &rows. Najkrócej po polsku — to nie jest pytanie o iteratory, tylko o własność. &rows daje iter() i tylko pożycza, &mut rows daje iter_mut() i pozwala zmieniać w miejscu, a gołe rows daje into_iter(), czyli przeniesienie własności (move): kolekcja zostaje rozebrana na części i po pętli już jej nie ma. Osoba przychodząca z Pythona wpada w to regularnie, bo tam for x in xs niczego nie konsumuje, a tutaj kompilator zaprotestuje dopiero przy następnym użyciu rows. Odruchowe dopisanie .clone(), żeby się skompilowało, prawie zawsze znaczy, że wybrano złe drzwi.
Trzecia obietnica tej strony dotyczy samej cechy (trait) Iterator: piszesz jedną metodę next, a siedemdziesiąt pięć pozostałych dostajesz gratis, bo trait definiuje je jako metody dostarczone (provided methods) zbudowane na next. Polskich materiałów jest tu naprawdę niewiele — polskie tłumaczenie Tour of Rust kończy się wcześniej, a rustc i std i tak mówią po angielsku — więc nazw adapterów i treści błędów szukaj po angielsku. Kolejność czytania warto zachować tę ze spisu: najpierw leniwość (laziness), potem trzy drzwi do kolekcji, potem pisanie własnego iteratora.
Szukaj po polsku: iteratory w Ruscie · leniwe wartościowanie · adaptery iteratorów · rust iterator adaptor vs consumer · rust iter vs into_iter