Einleitung: Warum will der Iterator nicht anhalten?
\n\nStellen Sie sich vor: Sie schreiben in Rust ein Programm, das eine Liste von Zahlen verarbeitet – Strings parst, Dateien liest, irgendwelche Transformationen durchführt. Sie verwenden Iterator::map, um jedes Element zu durchlaufen. Und dann – peng! – wird eines der Elemente zu einem Fehler Result::Err. Logischerweise sollte man die Iteration sofort stoppen und diesen Fehler nach oben zurückgeben. Aber map arbeitet einfach weiter, als ob nichts gewesen wäre. Warum? Und vor allem – wie behebt man das?
Diese Frage ist eine der beliebtesten auf Stack Overflow unter Rust-Anfängern. Und das zu Recht. Die Fehlerbehandlungsmechanismen in Rust sind flexibel, erfordern aber einen bewussten Ansatz. In diesem Artikel werden wir das Problem gründlich analysieren, mehrere Lösungen betrachten und die eleganteste auswählen. Los geht's!
\n\nDas Problem: map stoppt nicht
\n\nBeginnen wir mit einem klassischen Beispiel. Angenommen, wir haben einen Vektor von Strings, die wir in Zahlen umwandeln möchten:
\n\nfn main() {\n let inputs = vec!["42", "hello", "100"];\n\n let result: Vec<Result<i32, std::num::ParseIntError>> =\n inputs.iter().map(|s| s.parse::<i32>()).collect();\n\n println!("{:?}", result);\n}\n\nDieser Code wird kompiliert und ausgeführt. Aber was erhalten wir? Einen Vektor von Results: [Ok(42), Err(ParseIntError), Ok(100)]. Die Iteration wurde bei "hello" nicht gestoppt, sondern fortgesetzt. Für viele Aufgaben ist das in Ordnung – Sie möchten alle Fehler sammeln. Aber was, wenn Sie die erste Ausnahme benötigen? Zum Beispiel beim Laden einer Konfiguration: Wenn ein Parameter ungültig ist, macht es keinen Sinn, die weiteren zu überprüfen.
Das Problem ist, dass map eine faule Transformation ist. Es weiß nicht, was "Stopp" bedeutet. Es sagt einfach: "Gib mir ein Element, ich wende die Funktion an, gebe das Ergebnis zurück". Die gesamte Steuerung von Pause und Fortsetzung liegt beim Konsumenten des Iterators (collect, for, fold usw.). Und collect für Vec geht einfach bis zum Ende.
Lösung 1: collect mit FromIterator (die beliebteste)
\n\nDie häufigste Antwort auf Stack Overflow ist die Verwendung von collect::<Result<Vec<_>, _>>(). Rust weiß, dass man einen Iterator von Results in ein einzelnes Result mit einem Vektor darin sammeln kann. Wenn ein Err auftritt, wird die Sammlung abgebrochen und dieser Fehler zurückgegeben.
fn main() -> Result<(), std::num::ParseIntError> {\n let inputs = vec!["42", "hello", "100"];\n\n let result: Vec<i32> =\n inputs.iter()\n .map(|s| s.parse::<i32>())\n .collect::<Result<Vec<_>, _>>()?; // <-- Magie!\n\n println!("{:?}", result);\n Ok(())\n}\n\nWie funktioniert das? Der Typ Result<Vec<i32>, ParseIntError> implementiert das Trait FromIterator. Wenn collect ein Err sieht, gibt es dieses sofort zurück, ohne die verbleibenden Elemente zu verarbeiten. Wenn alles gut ist, erhalten Sie Ok(Vektor). Das ist kurz, idiomatisch und effizient.
Nachteil: Sie verlieren die Information, bei welchem Element der Fehler aufgetreten ist (es sei denn, Sie verpacken ihn in Ihren eigenen Typ). Aber für schnelle Skripte ist es ideal.
\n\nLösung 2: filter_map zum Ignorieren von Fehlern
\n\nManchmal möchten Sie nicht anhalten, sondern einfach fehlgeschlagene Elemente ignorieren. Zum Beispiel nur gültige Strings zählen. Dafür ist filter_map ideal geeignet:
fn main() {\n let inputs = vec!["42", "hello", "100", "abc", "7"];\n\n let numbers: Vec<i32> = inputs.iter()\n .filter_map(|s| s.parse::<i32>().ok()) // .ok() verwandelt Err in None\n .collect();\n\n println!("{:?}", numbers); // [42, 100, 7]\n}\n\nfilter_map akzeptiert