In Rust basiert die gesamte Standardkollektionbibliothek auf dem Konzept von Iteratoren. Ein Iterator ist ein Objekt, das das Trait Iterator implementiert, in dem die Methode next() definiert ist. Diese Methode gibt das nächste Element der Datenfolge zurück (Option<T>, wobei Some(T) der nächste Wert ist und None das Ende der Sequenz darstellt).
Beispiel eines Standarditerators:
let v = vec![1, 2, 3]; let mut iter = v.iter(); while let Some(x) = iter.next() { println!("{}", x); }
Iteratoradapter sind Methoden (z.B. .map(), .filter(), .enumerate(), .take()), die neue Iteratoren zurückgeben, die die Werte „on the fly“ gemäß den übergebenen Funktionen verarbeiten.
Benutzerdefinierte Iteratoren werden durch die Erstellung einer Struktur und die Implementierung des Traits Iterator mit eigenem Verhalten realisiert:
struct Counter { count: u8 } impl Iterator for Counter { type Item = u8; fn next(&mut self) -> Option<Self::Item> { if self.count < 5 { self.count += 1; Some(self.count) } else { None } } }
Verwenden Sie standardmäßige Adapter, wenn diese ausreichen, um Sammlungen zu verarbeiten. Die Implementierung eines eigenen Iterators ist notwendig, wenn:
Kann man unendliche Iteratoren erstellen? Was passiert, wenn man versucht, sie in einer Sammlung zu sammeln, z.B. mit
.collect()?
Antwort: Ja, in Rust gibt es Iteratoren wie std::iter::repeat, die eine unendliche Sequenz zurückgeben:
let mut endless = std::iter::repeat(1); endless.next(); // gibt Some(1) unendlich zurück
Wenn man versucht, einen solchen Iterator durch .collect() in eine Sammlung zu sammeln, wird das Programm hängen bleiben oder mit einem Speicherüberlauf abstürzen, weil die Iteration sich nicht von selbst beendet!
Geschichte
In einem REST API-Projekt wurde ein Array mit 1000 Elementen sortiert und dann .iter().filter(|x| *x > 500) verwendet, aber anstelle von .collect::<Vec<_>>() wurde innerhalb eines komplexen Adapters .fold(0, |acc, _| acc + 1) verwendet, wodurch das Verständnis darüber verloren ging, ob die Iteration korrekt abgeschlossen wird. Das Ergebnis: zufällige Hänger aufgrund der Tatsache, dass die Filterung durch einen uneingeschränkten faulen Iterator mit einem internen Fehler erfolgte.
Geschichte
In einer hauseigenen Engine zur Generierung von eindeutigen IDs beschloss ein zufälliger Entwickler, seinen eigenen Iterator zu implementieren, vergaß jedoch, None zurückzugeben, wenn das Limit erreicht war. Infolgedessen geriet die Iteration in eine unendliche Schleife, der Server im Produktivbetrieb verbrauchte die gesamte CPU und reagierte nicht auf Anfragen.
Geschichte
Im Frontend-Bereich (WebAssembly-Modul) verwendeten sie einen Iterator mit verschachtelten Adaptern: .map().filter().skip(), und versuchten, das Ergebnis über .collect() für ein Formular zu erhalten. Bei der Änderung des Typs unter dem Adapter gab Rust einen komplexen Kompilierungszeitfehler über Typinkongruenz aus, weil sie vergaßen, den genauen Typ der Sammlung anzugeben: .collect::<Vec<_>>(). Das Problem wurde durch das Hinzufügen einer Annotation gelöst, aber sie verbrachten mehrere Stunden mit der Fehlersuche.