Von Option zu Result in Rust: Tutorial ohne Makros

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Warum haben Sie es satt, Makros für Option in Result zu schreiben?

Sie programmieren in Rust und plötzlich stellen Sie fest: Sie haben ein Option<T>, brauchen aber ein Result<T, E>. Es scheint eine Kleinigkeit zu sein. Aber wenn Sie Anfänger sind, denken Sie zuerst: "Wahrscheinlich brauche ich ein Makro!" Oder Sie sind bereits ein erfahrener Entwickler, schreiben aber jedes Mal dasselbe: match option { Some(v) => Ok(v), None => Err("error") }. Geben Sie zu, das ermüdet.

Die Frage auf Stack Overflow "Is there a way to simplify converting an Option into a Result without a macro?" hat tausende Aufrufe gesammelt. Das ist kein Zufall: Das Problem ist jedem bekannt, der mit Rust arbeitet. Aber die gute Nachricht ist, dass es eine Lösung gibt, und sie ist überraschend einfach. In diesem Tutorial werden wir alle Methoden durchgehen, von den grundlegenden bis zu den eleganten, ohne ein einziges Makro.

Wir geben nicht nur die Antwort – wir zeigen, wie man wie ein Rust-Entwickler denkt. Los geht's!



Was sind Option und Result in Rust?

Bevor wir konvertieren, stellen wir sicher, dass wir auf derselben Wellenlänge sind.

  • Option<T> – ein Typ, der entweder Some(T) (Wert vorhanden) oder None (kein Wert) sein kann. Wird verwendet, wenn das Fehlen eines Werts eine normale Situation ist (z. B. Suche nach einem Element in einer Sammlung).
  • Result<T, E> – ein Typ, der entweder Ok(T) (alles gut) oder Err(E) (ein Fehler ist aufgetreten) sein kann. Wird für Operationen verwendet, die fehlschlagen können (z. B. Lesen einer Datei).

Das Problem: Manchmal haben Sie ein Option, aber laut Programmlogik ist das Fehlen eines Werts ein Fehler. Genau hier ist eine Konvertierung nötig.



Grundlegende Methode: match (aber das wissen Sie bereits)

Die offensichtlichste und von Anfängern am häufigsten verwendete Methode ist match. Ja, sie funktioniert, aber sie ist wortreich:

fn get_user_name(id: u32) -> Option<String> {    // ... irgendeine Logik    if id == 1 { Some("Alice".to_string()) } else { None }}

fn main() { let user_id = 2; let name = match get_user_name(user_id) { Some(name) => Ok(name), None => Err("User not found"), }; println!("{:?}", name); // Err("User not found")}

Dieser Code ist korrekt, aber wenn es Dutzende solcher Konvertierungen im Projekt gibt, werden Sie es leid, match zu schreiben. Außerdem kann man leicht einen Fehler machen und vergessen, None zu behandeln. Es braucht etwas Prägnanteres.



Die Methode ok_or: Ihr neuer bester Freund

Die Standardbibliothek von Rust bietet die Methode ok_or für Option. Sie macht genau das, was wir brauchen: Sie wandelt Some(v) in Ok(v) um und None in Err(error), wobei error der Wert ist, den Sie übergeben.

fn main() {    let user_id = 2;    let name = get_user_name(user_id).ok_or("User not found");    println!("{:?}", name); // Err("User not found")}

Nur eine Zeile! Aber es gibt einen Haken: ok_or nimmt einen Wert im Besitz (owned value). Wenn Sie ein String-Literal wie im Beispiel übergeben, funktioniert das, weil &str Into<String> implementiert. Aber wenn Sie einen Fehler übergeben müssen, der bereits irgendwo gespeichert ist, kann dies zu unnötigem Kopieren führen.

Für solche Fälle gibt es ok_or_else – es nimmt ein Closure, das nur aufgerufen wird, wenn Option gleich None ist. Das spart Ressourcen:

fn main() {    let user_id = 2;    let error_msg = format!("User with id {} not found", user_id);    let name = get_user_name(user_id).ok_or_else(|| error_msg.clone());    // oder noch besser:    let name = get_user_name(user_id).ok_or_else(|| format!("User {} not found", user_id));    println!("{:?}", name);}

Tipp: Verwenden Sie ok_or, wenn die Fehlermeldung eine Konstante ist. Verwenden Sie ok_

Blogs

Buchempfehlungen