Skip to content

Modul 5 — Enums & Pattern Matching

Varianten mit Daten, Option und Result statt null und Exceptions, match bis zur Erschöpfung — und der ?-Operator für saubere Fehlerflüsse.

Fortschritt0/4 Lektionen
17

Enums: Varianten mit Daten

Lektion 17 von 4

Aus Modul 4 kennst du Structs: alle Felder gleichzeitig. Ein Enum ist das Gegenteil: entweder/oder. Ein Wert ist genau eine von mehreren Varianten — und jede Variante kann ihre eigenen Daten tragen:

enum Shape {
    Circle { radius: f64 },
    Rectangle(f64, f64),
    Point,
}

fn area(s: Shape) -> f64 {
    match s {
        Shape::Circle { radius } => 3.14159 * radius * radius,
        Shape::Rectangle(w, h) => w * h,
        Shape::Point => 0.0,
    }
}

fn main() {
    println!("{}", area(Shape::Circle { radius: 2.0 }));
    println!("{}", area(Shape::Rectangle(3.0, 4.0)));
}

Drei Varianten, drei verschiedene Formen von Daten: struct-artig (benannte Felder), tuple-artig (Positionen) und ganz ohne Daten. So modellierst du „eins von“ ohne Kompromisse.

Enums verhalten sich wie Typen

Auch Enums bekommen impl-Blöcke mit Methoden — das Verhalten hängt an der Familie, nicht an der einzelnen Variante:

enum Response {
    Ok,
    NotFound,
    ServerError(String),
}

impl Response {
    fn is_success(&self) -> bool {
        matches!(self, Response::Ok)
    }
}

fn main() {
    let r = Response::ServerError(String::from("DB down"));
    println!("{}", r.is_success());
}

matches! ist ein Makro für die kompakte Ja/Nein-Frage: „Ist das dieser Variant?“ Für alles Detaillierteres nimm ein echtes match.

Enum vs. Struct — die Modellierungsfrage

  • Struct: „das Ding HAT all diese Daten“ (gleichzeitig)
  • Enum: „das Ding IST genau eins von diesen“ (alternativ)

HTTP-Antworten sind ein Enum (Ok ODER NotFound ODER ServerError); eine HTTP-Antwort mit Headern und Body wäre ein Struct.

Praxis-Szenario: Die Statuscodes

Dein API-Client kennt bisher nur Ok und Fehler. Das Monitoring will mehr: 404 von 500 von Timeouts unterscheiden. Strings als Status schienen einfach — bis Timeout mit Leerzeichen dreimal produziert wurde.

Übung: HTTP-Linien

🧩 Übung m5-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:

18

Option & Result — kein null, keine Exceptions

Lektion 18 von 4

Die zwei wichtigsten Enums der ganzen Sprache sind Standard: Option und Result. Sie ersetzen null und Exceptions — durch Typen.

fn find_price(catalog: &[(&str, u32)], article: &str) -> Option<u32> {
    for (name, price) in catalog {
        if *name == article {
            return Some(*price);
        }
    }
    None
}

fn main() {
    let catalog = [("Kaffee", 7), ("Tee", 4)];
    match find_price(&catalog, "Tee") {
        Some(price) => println!("Preis: {price}"),
        None => println!("unbekannt"),
    }
}

Option ist Some(wert) oder None. Es gibt keinen dritten Weg — und der Compiler verlangt, dass du beide Fälle bedachtest, bevor du an den Wert kommst. Der Milliarden-Dollar-Fehler „null reference“ existiert in Rust nicht.

Result: Fehler sind Werte

fn parse_age(s: &str) -> Result<u32, String> {
    match s.parse::<u32>() {
        Ok(age) => Ok(age),
        Err(_) => Err(String::from("keine gültige Zahl")),
    }
}

fn main() {
    println!("{:?}", parse_age("42"));
    println!("{:?}", parse_age("abc"));
}

Result<T, E> ist Ok(T) oder Err(E) — für alles, was scheitern kann und soll: Parsen, Dateien, Netzwerk. Der Aufrufer KANN den Fehler nicht ignorieren, ohne es explizit zu tun.

Panic ist kein Fehlerhandling

panic! bricht ab — für echte Bugs und gebrochene Invarianten. Alles, was erwartbar schiefgehen kann (falsche Eingabe, fehlende Datei), gehört in Result. unwrap() auf einem Err oder None paniziert: gut für Tests und Prototypen, tabu in Produktion.

Übung: Suche ohne Panic

🧩 Übung m5-l2-e1

🔮 Vorhersage: Was passiert beim Ausführen?

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

✅ Wichtige Erkenntnisse

0/4

Haken setzen, um deinen Lernfortschritt zu markieren:

19

Match meistern: Muster, Guards, Bindings

Lektion 19 von 4

match ist mächtiger als switch: Muster destrukturieren, Guards filtern, Bindings fangen Werte. Ein Überblick über die Werkzeuge:

fn classify(temp: i32) -> String {
    match temp {
        x if x < 0 => String::from("frost"),      // Guard
        n @ 0..=30 => format!("okay ({n} Grad)"), // Range + @-Binding
        _ => String::from("heiß"),
    }
}

fn main() {
    println!("{}", classify(-5));
    println!("{}", classify(21));
    println!("{}", classify(35));
}

Destructuring: Muster öffnen Varianten

enum Message {
    Move { x: i32, y: i32 },
    Say(String),
    Ping,
}

fn handle(msg: Message) {
    match msg {
        Message::Move { x, y } => println!("gehe zu {x},{y}"),
        Message::Say(text) if text.len() > 20 => println!("langer Text"),
        Message::Say(text) => println!("sage: {text}"),
        Message::Ping => println!("pong"),
    }
}

fn main() {
    handle(Message::Move { x: 3, y: 7 });
    handle(Message::Say(String::from("hallo")));
}

Das Muster Move { x, y } nimmt die Felder auseinander — ohne Zwischenvariablen. Ein Guard (if text.len() > 20) filtert innerhalb eines Arms; der Compiler prüft weiter auf Erschöpfung.

Erschöpfung ist das Superkonzept

match MUSS jeden Fall abdecken. Fügt dein Team morgen eine Variante hinzu, bricht jede nicht angepasste Stelle beim Kompilieren — statt zur Laufzeit stillschweigend falsch zu laufen. _ deaktiviert diese Prüfung bewusst: Nutze es für „der Rest interessiert nicht“, nie aus Bequemlichkeit.

Match-WerkstattBereit
Editor wird geladen…

if let und let else

Nur EIN Fall interessiert? if let ist das kompakte match:

fn main() {
    let maybe = Some(42);
    if let Some(n) = maybe {
        println!("Wert: {n}");
    }

    let maybe2: Option<i32> = None;
    let doubled = if let Some(n) = maybe2 {
        n * 2
    } else {
        0
    };
    println!("{doubled}");
}

Und seit Rust 1.65 gibt es let ... else für das frühzeitige Ausscheiden aus der Funktion, wenn der interessante Fall fehlt.

Übung: Klassifikator

🧩 Übung m5-l3-e1

🔮 Vorhersage: Was passiert beim Ausführen?

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

✅ Wichtige Erkenntnisse

0/4

Haken setzen, um deinen Lernfortschritt zu markieren:

20

Combinatoren & der ?-Operator

Lektion 20 von 4

Option und Result haben eine ganze Werkzeugkiste von Methoden, die das match-Boilerplate ersetzen — Combinatoren:

fn main() {
    let maybe: Option<i32> = Some(5);

    // map: transformiere den Wert, wenn vorhanden
    println!("{:?}", maybe.map(|n| n * 2));

    // unwrap_or / unwrap_or_else: Fallback statt Panic
    println!("{}", maybe.unwrap_or(0));
    println!("{}", None::<i32>.unwrap_or_else(|| expensive()));

    // ok_or: Option in Result mit Fehlermeldung
    let r = maybe.ok_or(String::from("fehlt"));
    println!("{r:?}");
}

fn expensive() -> i32 {
    42
}

?: der Early-Return für Fehler

Der große Wurf ist der ?-Operator: „Wenn Ok/Some, nimm den Wert. Wenn nicht, verlasse SOFORT die Funktion mit dem Fehler.“

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

fn main() {
    println!("{:?}", parse_pair("12, -3"));
    println!("{:?}", parse_pair("7,x"));
}

Drei Fehlerquellen, drei ? — null Boilerplate. Der Fehlerflusse liest sich wie der Happy-Path, und jede Fehlermeldung ist absichtlich. map_err übersetzt dabei fremde Fehlertypen in deine.

Die Regel für sauberen Fehlerfluss

  1. Innere Funktionen geben Result zurück (klein, testbar).
  2. Fehler mit ? propagieren, mit map_err verständlich machen.
  3. Nur die äußerste Schicht (main, Handler) entscheidet: Ausgeben, Loggen, Retry — niemals unwrap in der Mitte.

Übung: von unwrap zu ?

🧩 Übung m5-l4-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:

Modul-Checkpoint

Checkpoint: Modul 5

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.