Einführung
Einige Fehler sind Programmierfehler. Andere treten jedoch erwartungsgemäß zur Laufzeit auf: Eine Person kann beispielsweise fehlerhaften Text oder einen Wert außerhalb des erlaubten Bereichs eingeben. Rust stellt solche wiederherstellbaren Ergebnisse mit Result<T, E> dar. Dieser Typ ist entweder Ok(T) oder Err(E).
Sie werden gültigen und ungültigen Text ohne Panik verarbeiten, aussagekräftige Validierungsfehler zurückgeben und den ?-Operator verwenden, um Fehler an den Aufrufer weiterzuleiten. Das vorbereitete Cargo-Projekt hält jede Änderung auf die Fehlerbehandlung fokussiert.
Ein erfolgreiches Result abgleichen
In diesem Schritt analysieren Sie Text als Ganzzahl und gleichen das erfolgreiche Ergebnis ab.
Das Projekt befindet sich unter /home/labex/project/result-workshop. Die Datei src/main.rs enthält vier gezielte Platzhalter. Wechseln Sie in das Projektverzeichnis und öffnen Sie die Quelldatei:
cd /home/labex/project/result-workshop
nano src/main.rs
Ersetzen Sie den Kommentar für Schritt 1 durch:
let valid = "42".parse::<i32>();
match valid {
Ok(value) => println!("Parsed value: {value}"),
Err(error) => println!("Parse error: {error}"),
}
parse ist eine wiederverwendbare Methode, die unterschiedliche Ausgabetypen liefern kann. Der Teil ::<i32> gibt den Ausgabetyp für diesen Aufruf an. Dadurch weist Rust den String als vorzeichenbehaftete 32-Bit-Ganzzahl zu. Dieses Typargument in spitzen Klammern unterscheidet sich von einem Pfad wie Message::Text, obwohl beide :: verwenden. Die Analyse kann fehlschlagen, daher ist der Rückgabetyp ein Result. Ok(value) enthält die analysierte Ganzzahl, während Err(error) Informationen über den Grund des Fehlers enthält. Ein match muss beide Varianten behandeln.
Speichern Sie mit Strg+O, drücken Sie die Eingabetaste und beenden Sie den Editor mit Strg+X. Prüfen und starten Sie das Programm:
cargo check
cargo run --quiet
Parsed value: 42
Nur der Ok-Arm wurde ausgeführt, weil "42" ein gültiger Ganzzahltext ist.
Ungültige Eingaben behandeln
In diesem Schritt behandeln Sie fehlerhaften Text als Daten, anstatt das Programm dadurch abstürzen zu lassen.
Eine Panik zeigt an, dass ein Programm einen Zustand erreicht hat, den sein Entwurf nicht erwartet hat. Normalerweise wird die Ausführung dadurch beendet. Fehlerhafte externe Eingaben sind jedoch erwartbar. Sie sollten daher als Err erhalten bleiben, damit Ihr Programm sie untersuchen und melden kann. Öffnen Sie die Quelldatei:
nano src/main.rs
Ersetzen Sie den Kommentar für Schritt 2 durch:
let invalid = "many".parse::<i32>();
match invalid {
Ok(value) => println!("Unexpected value: {value}"),
Err(error) => println!("Handled invalid input: {error}"),
}
Dieser match deckt weiterhin beide möglichen Ergebnisse ab. Der vorbereitete Text führt jedoch zur Ausführung des Err-Arms. Speichern und beenden Sie den Editor. Starten Sie anschließend das Programm:
cargo run --quiet
Die zweite Zeile sollte lauten:
Handled invalid input: invalid digit found in string
Das Programm wurde normal fortgesetzt und hat den Fehler des Parsers in eine hilfreiche Meldung umgewandelt. Vermeiden Sie unwrap() bei externen Daten: unwrap() würde bei demselben Err eine Panik auslösen, anstatt Ihnen die Fehlerbehandlung zu ermöglichen.
Mit dem Fragezeichen-Operator analysieren und validieren
In diesem Schritt erstellen Sie eine Funktion, die entweder einen Prozentsatz oder eine aussagekräftige Fehlermeldung zurückgeben kann.
Der Typ Result<u32, String> bedeutet: Bei Erfolg enthält das Ergebnis eine vorzeichenlose Ganzzahl, bei einem Fehler einen eigenen Fehlertext. Der ?-Operator übernimmt den Wert aus Ok. Wenn er Err erkennt, gibt er diesen Fehler sofort aus der aktuellen Funktion zurück.
Öffnen Sie die Quelldatei:
nano src/main.rs
Fügen Sie diese Funktion oberhalb von main ein:
fn parse_percentage(text: &str) -> Result<u32, String> {
let value = text
.parse::<u32>()
.map_err(|_| format!("not a whole number: {text}"))?;
if value <= 100 {
Ok(value)
} else {
Err(format!("outside 0..=100: {value}"))
}
}
Lesen Sie die Funktion als kurze Abfolge. Zuerst versucht parse::<u32>(), den Text in eine Ganzzahl umzuwandeln. Anschließend ändert map_err(...) nur einen Analysefehler. Es erzeugt einen verständlicheren String, der die ursprüngliche Eingabe enthält.
Danach entpackt ? entweder die erfolgreiche Zahl für value oder gibt den neuen Fehler sofort aus parse_percentage zurück. Wenn die Analyse erfolgreich war, validiert if die Zahl und gibt entweder Ok(value) oder bei einem Wert außerhalb des erlaubten Bereichs ein Err zurück.
Der Closure-Parameter _ bedeutet, dass der ursprüngliche Fehlerwert des Parsers bewusst ignoriert wird, weil die neue Meldung ihn ersetzt. Hier ist _ ein ignorierter Parameter. Zuvor war _ => ein Wildcard-Arm in einem match, und let _ = hat ein vollständiges Ergebnis verworfen.
Ersetzen Sie den Kommentar für Schritt 3 innerhalb von main durch:
println!("85 => {:?}", parse_percentage("85"));
println!("150 => {:?}", parse_percentage("150"));
println!("many => {:?}", parse_percentage("many"));
Der Formatierer :? zeigt in diesem Lernversuch die Variante Ok oder Err an. Speichern und beenden Sie den Editor. Prüfen und starten Sie anschließend das Programm:
cargo check
cargo run --quiet
Die neuen Zeilen sollten lauten:
85 => Ok(85)
150 => Err("outside 0..=100: 150")
many => Err("not a whole number: many")
Die Funktion unterscheidet nun zwischen einem gültigen Wert, einem analysierten, aber außerhalb des erlaubten Bereichs liegenden Wert und fehlerhaftem Text.
Einen Fehler durch eine weitere Funktion weiterleiten
In diesem Schritt verwenden Sie ? an einer zweiten Funktionsgrenze und behandeln das endgültige Ergebnis in main.
Eine Hilfsfunktion weiß nicht immer, wie eine Anwendung einen Fehler anzeigen oder behandeln soll. Sie kann Err nach oben weiterleiten, sodass der Aufrufer entscheidet. Öffnen Sie die Quelldatei:
nano src/main.rs
Fügen Sie diese Funktion zwischen parse_percentage und main ein:
fn acceptance_message(text: &str) -> Result<String, String> {
let value = parse_percentage(text)?;
Ok(format!("accepted {value}%"))
}
Wenn die Analyse oder Validierung fehlschlägt, gibt ? den vorhandenen String-Fehler sofort zurück. Bei Erfolg verpackt die Funktion ihre Meldung in Ok.
Ersetzen Sie den Kommentar für Schritt 4 innerhalb von main durch:
for text in ["73", "bad"] {
match acceptance_message(text) {
Ok(message) => println!("{text} => {message}"),
Err(message) => println!("{text} => error: {message}"),
}
}
Die Schleife verwendet eine erfolgreiche und eine fehlschlagende Eingabe. main bildet die Grenze, an der jedes Ergebnis in eine für Benutzer bestimmte Ausgabe umgewandelt wird. Speichern und beenden Sie den Editor. Prüfen und starten Sie anschließend das Programm:
cargo check
cargo run --quiet
Die letzten Zeilen sollten lauten:
73 => accepted 73%
bad => error: not a whole number: bad
Der von parse_percentage erzeugte Fehlerkontext blieb bei der Weiterleitung durch acceptance_message erhalten.
Zusammenfassung
Sie haben erfolgreiche und fehlgeschlagene Result-Werte abgeglichen, fehlerhafte Eingaben als wiederherstellbaren Zustand behandelt, mit map_err kontextbezogene String-Fehler erzeugt und Fehler mit ? über Funktionsgrenzen weitergeleitet, anstatt mit unwrap eine Panik auszulösen.


