Skip to content
Zurück zu den Lernmaterialien

Modul 5 — Enums & Pattern Matching

6. September 2026~3 min read
Lektion von 0/ abgeschlossen
Modul 5 — Enums & Pattern Matching

Enums: Varianten mit Daten

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.

📝 Schnellprüfung

Welche Variantenformen kann ein Rust-Enum haben?

Übung: HTTP-Linien

🧩 Übung m5-l1-e1

🔮 Vorhersage: Was passiert beim Ausführen?

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

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

    Lektion von 0/ abgeschlossen
    Modul 5 — Enums & Pattern Matching

    Option & Result — kein null, keine Exceptions

    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.

    📝 Schnellprüfung

    Warum ist .unwrap() auf einem Result in Produktionscode gefährlich?

    Ü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

    Haken setzen, um deinen Lernfortschritt zu markieren:

      Lektion von 0/ abgeschlossen
      Modul 5 — Enums & Pattern Matching

      Match meistern: Muster, Guards, Bindings

      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.

      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.

      📝 Schnellprüfung

      Was macht `n @ 1..=12` im Muster?

      Übung: Klassifikator

      🧩 Übung m5-l3-e1

      🔮 Vorhersage: Was passiert beim Ausführen?

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

      ✅ Wichtige Erkenntnisse

      Haken setzen, um deinen Lernfortschritt zu markieren:

        Lektion von 0/ abgeschlossen
        Modul 5 — Enums & Pattern Matching

        Combinatoren & der ?-Operator

        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.

        📝 Schnellprüfung

        Was passiert bei `expr?`, wenn expr ein Err ist?

        Ü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

        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.

          Bereit, dein Wissen zu testen?

          Probiere unsere Übungsprüfungen mit Hunderten von realistischen Fragen aus.

          Üben starten →

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