Modul 10 — Closures & Iteratoren
Closures: Funktionen mit Gedächtnis
Manchmal brauchst du eine kleine Funktion genau an einer Stelle — ein Vergleichskriterium, ein Bonus-Rechner, ein Callback. Dafür sind Closures da: Funktionen, die ihre Umgebung mit einfangen.
fn main() {
let basis = 7;
let verdopple_mit = |x: i32| x * basis;
println!("{}", verdopple_mit(6));
}
Die Closure fängt basis aus der Umgebung ein — ohne dass du es
deklarieren musst. Der Compiler leitet Typen her und entscheidet, WIE
gefangen wird: per Referenz zum Lesen, per Referenz zum Ändern oder — mit
move — per Besitz. Closures, die ihre Umgebung verändern, implementieren
FnMut und brauchen eine mut-Bindung:
fn main() {
let mut punkte = 0;
let mut punkte_dazu = |bonus: i32| punkte += bonus;
punkte_dazu(5);
punkte_dazu(3);
println!("Punkte: {punkte}");
}
Die drei Closure-Traits
- FnOnce — ruft sich selbst auf und verbraucht dabei Captures (z. B. bei move)
- Fn — liest Captures, beliebig oft aufrufbar
- FnMut — ändert Captures, braucht eine mut-Bindung
moveerzwingt Besitz statt Leihen — Pflicht bei Threads (Modul 12!)
Probiere es aus: baue eine Closure, die eine Eingabe mit deinem persönlichen Faktor multipliziert.
📝 Schnellprüfung
Warum braucht punkte_dazu eine mut-Bindung, wenn die Closure punkte verändert?
Übung: Closures fangen
🧩 Übung m10-l1-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Der Iterator-Trait: next() ist alles
Hinter jedem for-loop steckt ein Iterator — ein Objekt mit einer einzigen Methode:
struct Zaehler {
bis: u32,
aktuell: u32,
}
impl Iterator for Zaehler {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.aktuell < self.bis {
self.aktuell += 1;
Some(self.aktuell)
} else {
None
}
}
}
fn main() {
for n in Zaehler { bis: 4, aktuell: 0 } {
println!("{n}");
}
}
next() liefert Some(Item) pro Element und None am Ende — mehr ist der
ganze Vertrag nicht. Implementierst du Iterator für deinen Typ, bekommst
du alle Adapter-Methoden der Standardbibliothek gratis: map, filter,
sum, collect, take, zip ... Die ~70 Methoden haben alle dieselbe Basis.
Und wichtig: Iteratoren sind lazy. (1..=1_000_000) rechnet noch
nichts — erst ein Verbraucher treibt die Kette an.
📝 Schnellprüfung
Was passiert, wenn du next() auf einem erschöpften Iterator aufrufst?
Übung: Iterator selbst bauen
🧩 Übung m10-l2-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Adapter-Ketten: filter, map, collect
Jetzt der Lohn: Adapter verketten sich zu Pipelines — und keine Stufe kopiert etwas.
fn main() {
let zahlen = vec![1, 2, 3, 4];
let zehner: Vec<u32> = zahlen
.iter()
.filter(|z| *z % 2 == 0)
.map(|z| z * 10)
.collect();
println!("{zehner:?}");
}
Drei Rollen in der Kette:
- Adapter (lazy) —
filter,map,take,cloned: bauen nur die Pipeline-Beschreibung auf - Verbraucher (tatkräftig) —
collect,sum,count,max,any: treiben next() an, bis None kommt - collect — sammelt in fast jeden Zieltyp: Vec, String, sogar ein Result über die ganze Kette
Letzteres ist der Geheimtipp: collect::<Result<Vec<u32>, String>>()
liefert entweder alle Werte — oder den ersten Fehler. Eine Pipeline von
Results wird zum Result einer Pipeline.
Und weil alles Slices leihen statt kopieren: die Kette oben verbraucht
zahlen nicht — sie leiht. Ein into_iter() wäre die besitzende Variante.
📝 Schnellprüfung
Was liefert (1..=5).map(|x| x * x).collect::<Result<Vec<u32>, String>>()?
Übung: Pipeline bauen
🧩 Übung m10-l3-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Mini-Projekt: Log-Auswertung
Erst das Szenario aus dem Arbeitsalltag — dann die Übung:
🏢 Praxis-Szenario: Die Log-Datei in der Konsole
Nach jedem Deployment sichtet dein Team per Hand die Logdatei: Welche Fehler gab es? Welche Zeile ist die längste, weil dort vermutlich ein Timeout gedruckt wurde? Kollegen kopieren Zeilen in Excel, andere schreiben Shell-Pipelines mit grep und awk — niemand kann die beiden Antworten in einem Rutsch aus dem Programm ziehen.
🧩 Übung m10-l4-e1
🔮 Vorhersage: Was passiert beim Ausführen?
Was du in Modul 10 geschafft hast
- Closures fangen Umgebung ein — Fn/FnMut/FnOnce bestimmen die Aufruf-Regeln
- Eigene Iteratoren: next() implementieren, alle Adapter erben
- Pipelines ohne Kopien — lazy bis zum Verbraucher
- Eine Auswertung, die zwei Fragen in einer Zeile je beantwortet
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Modul-Checkpoint
Checkpoint: Modul 10
Bestehensgrenze: 80 %. Der Versuch wird bewertet und zählt für das Modul-Gate.
Bereit, dein Wissen zu testen?
Probiere unsere Übungsprüfungen mit Hunderten von realistischen Fragen aus.
Üben starten →