Skip to content

Modul 7 — Fehlerbehandlung

Fehler als Werte: eigene Error-Enums, der ?-Operator, Panic vs. Result — und ein Config-Parser als Mini-Projekt.

Fortschritt0/4 Lektionen
25

Fehler typisieren: eigene Error-Enums

Lektion 25 von 4

In Modul 5 kennengelernt: Result als Aufschrift "kann schiefgehen". Bisher war der Fehlerteil oft ein bloßer String — schnell, aber unstrukturiert. Ein eigenes Error-Enum macht jede Fehlerart zu einem benannten Wert, auf den Caller gezielt reagieren können:

#[derive(Debug, PartialEq)]
enum ParseError {
    InvalidLine(String),
    NotANumber(String),
}

fn parse_line(line: &str) -> Result<(String, u32), ParseError> {
    let (key, value) = line
        .split_once('=')
        .ok_or_else(|| ParseError::InvalidLine(line.to_string()))?;
    let num = value
        .trim()
        .parse::<u32>()
        .map_err(|_| ParseError::NotANumber(line.to_string()))?;
    Ok((key.trim().to_string(), num))
}

fn main() {
    match parse_line("timeout=30") {
        Ok((key, value)) => println!("{key} = {value}"),
        Err(e) => println!("Fehler: {e:?}"),
    }
}

Zwei Dinge passieren hier: ok_or_else verwandelt das Option von split_once in ein Result mit unserer Fehlerart, und map_err übersetzt den Fremdfehler des Parsers (ParseIntError) in unser Enum. Am Ende hat die ganze Funktion genau einen Fehlertyp.

Warum kein String als Fehlertyp?

  • Ein String kann alles behaupten — ein Enum hat fest definierte Varianten
  • Der Compiler erzwingt: wer matcht, behandelt jede Fehlerart (Exhaustivität)
  • Tests können Fehler exakt vergleichen (assert_eq! mit PartialEq)
  • Der Kontext (die Zeile) reist als Datenfeld im Variant mit

Übung: die zweite Variante

🧩 Übung m7-l1-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

26

Der ?-Operator: Fehler elegant weiterreichen

Lektion 26 von 4

Funktionen, die selbst nichts Sinnvolles mit einem Fehler anfangen können, reichen ihn nach oben weiter. Mit match wird das eine Match-Pyramide — mit dem ?-Operator eine Zeile pro Schritt:

fn parse_number(s: &str) -> Result<i32, String> {
    s.trim()
        .parse::<i32>()
        .map_err(|_| String::from("keine Zahl"))
}

fn parse_pair(s: &str) -> Result<(i32, i32), String> {
    let (a, b) = s
        .split_once(',')
        .ok_or_else(|| String::from("Komma fehlt"))?;
    let x = parse_number(a)?;
    let y = parse_number(b)?;
    Ok((x, y))
}

fn main() -> Result<(), String> {
    let (x, y) = parse_pair("3,4")?;
    println!("Paar: {x}, {y}");
    Ok(())
}

Das ? bedeutet: Ok-Wert auspacken und weitermachen — oder den Fehler sofort aus der Funktion zurückgeben. Und sogar main darf Result zurückgeben: bei Err beendet sich das Programm mit einem Nicht-Null-Exitcode und druckt den Fehler — genau das, was Skripte und CI von einem CLI-Tool erwarten.

Die drei ?-Regeln

  • Die umgebende Funktion muss selbst Result (oder Option) zurückgeben
  • Unterschiedliche Fehlertypen? ? konvertiert automatisch per From — oder du übersetzt vorher mit map_err
  • ? ist für Propagieren da; die Entscheidung (melden, retry, abbrechen) fällt ganz oben
let x = parse_number(a)?;
&ax

? entpackt Ok(42) → x besitzt den Wert. Bei Err verlässt ? sofort die Funktion.

Schritt 1 von 3

Probiere es selbst: schreibe eine Funktion doppelt(eingabe: &str), die die Eingabe als Zahl parst, verdoppelt und als Result zurückgibt — mit ? statt match.

? selbst ausprobierenBereit
Editor wird geladen…

Übung: die Match-Pyramide abbauen

🧩 Übung m7-l2-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

27

Panic oder Result?

Lektion 27 von 4

Rust kennt zwei Fehlerwelten: Panic (unwideruflich: das Programm bricht ab) und Result (wiederherstellbar: der Caller entscheidet). Die Faust- regel der Praxis:

  • Panic — nur für Bugs und gebrochene Invarianten: ein Index außerhalb des Vec, eine Konfiguration, die schon beim Start belegt, dass der Code falsch ist
  • Result — für alles Erwartbare: fehlende Datei, kaputte Nutzereingabe, Timeout im Netzwerk
fn lade_konfig(pfad: &str) -> Result<String, String> {
    match std::fs::read_to_string(pfad) {
        Ok(inhalt) => Ok(inhalt),
        Err(e) => Err(format!("Konfiguration nicht lesbar: {e}")),
    }
}

fn main() {
    match lade_konfig("app.conf") {
        Ok(inhalt) => println!("{} Bytes geladen", inhalt.len()),
        Err(msg) => {
            eprintln!("Start abgebrochen: {msg}");
            std::process::exit(1);
        }
    }
}

.expect("Kontext") ist der ehrlichere Bruder von .unwrap(): bei Panic druckt er den Kontext mit — aber beides gehört nicht auf Pfade, deren Scheitern du erwartest. Wenn es doch mal knallt, hilft:

RUST_BACKTRACE=1 ./meinprogramm

Der Backtrace zeigt die Aufrufliste im Panic-Moment — die Spur von der kaputten Stelle zurück zu dir.

Übung: kein Panic mehr

🧩 Übung m7-l3-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

28

Mini-Projekt: Config-Parser

Lektion 28 von 4

Alles aus dem Modul in einem Baustein: ein Parser für key=wert-Zeilen, der Fehler typisiert meldet und nie paniziert. Das Szenario:

Praxis-Szenario: Die Server-Konfiguration

Dein Server liest seine Einstellungen aus einer Textdatei: timeout=30, retries=5. Ein Kollege editiert die Datei von Hand — und vertippt sich. Bisher: das Programm paniziert beim Start mitten in der Nacht, ohne Hinweis, welche Zeile schuld war. Dein Vorschlag: ein Parser, der jede Zeile in ein Ergebnis mit klarer Fehlerart übersetzt und kaputte Zeilen benennt, statt unterzugehen.

Die Übung baut genau diesen Parser — vom Enum bis zur Ausgabeschleife:

🧩 Übung m7-l4-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

Was du in Modul 7 geschafft hast

  • Fehlerarten als Varianten — der Compiler zwingt zur vollständigen Behandlung
  • ? macht Propagieren zur One-Liner; main als Result nutzt Exitcodes
  • Panic ist die Ausnahme für Bugs — nicht die Norm für Fehler
  • Ein Parser, der kaputte Eingaben benennt statt abzustürzen

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

Modul-Checkpoint

Checkpoint: Modul 7

Bestehensgrenze: 80 %. Der Versuch wird bewertet und zählt für das Modul-Gate.

This site uses essential cookies for Stripe payments. No tracking cookies.