Skip to content
Zurück zu den Lernmaterialien

Modul 8 — Traits & Generics

6. September 2026~3 min read
Lektion von 0/ abgeschlossen
Modul 8 — Traits & Generics

Generics: Typen als Parameter

Eine Funktion für jede Typvariante? largest_i32, largest_char, largest_string — dreimal derselbe Code. Generics machen den Typ zum Parameter. Aber Achtung: ohne Zusagen kompiliert nicht einmal ein Vergleich:

fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut max = &list[0];
    for item in list {
        if item > max {
            max = item;
        }
    }
    max
}

fn main() {
    let zahlen = vec![3, 9, 4];
    let zeichen = vec!['y', 'a', 'm'];
    println!("{}", largest(&zahlen));
    println!("{}", largest(&zeichen));
}

Das : PartialOrd ist ein Trait-Bound — eine Zusage an den Compiler: "jedes T, mit dem du diese Funktion instanziierst, kann verglichen werden". Ohne den Bound würde die Zeile item > max den Compiler stoppen: er kennt die Fähigkeiten von T nicht.

Auch Structs können generisch sein — mit so vielen Typparametern wie Felder brauchen:

struct Point<T, U> {
    x: T,
    y: U,
}

fn main() {
    let ganzzahlig = Point { x: 3, y: -2 };
    let gemischt = Point { x: 1, y: 0.5 };
    println!("{:?} und {:?}", ganzzahlig, gemischt);
}

Der Typ von gemischt wird inferiert — die Parameter T und U füllen sich automatisch. Und das Beste: Generics kosten nichts zur Laufzeit. Der Compiler erzeugt pro verwendetem konkreten Typ eine spezialisierte Kopie (Monomorphisierung) — derselbe Maschinencode, als hättest du jede Funktion von Hand geschrieben.

📝 Schnellprüfung

Was leistet Monomorphisierung?

Übung: der Bound fehlt

🧩 Übung m8-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 8 — Traits & Generics

    Traits: gemeinsames Verhalten

    Generics sagen "irgendein Typ". Traits sagen "irgendein Typ, der DIES kann" — und definieren, was DIES bedeutet. Ein Trait ist ein Vertrag aus Methodensignaturen:

    trait Summarize {
        fn author(&self) -> &str;
        fn title(&self) -> &str;
    
        fn summary(&self) -> String {
            format!("{} von {}", self.title(), self.author())
        }
    }
    
    struct Article {
        title: String,
        author: String,
    }
    
    impl Summarize for Article {
        fn author(&self) -> &str {
            &self.author
        }
        fn title(&self) -> &str {
            &self.title
        }
    }
    
    fn notify(item: &impl Summarize) {
        println!("Neu: {}", item.summary());
    }
    
    fn main() {
        let a = Article {
            title: String::from("Rust im Browser"),
            author: String::from("Tobias"),
        };
        notify(&a);
    }
    

    Drei Konzepte in einem Beispiel: summary hat einen Rumpf — eine Default-Implementierung, die Article erbt, ohne sie zu schreiben. impl Summarize for Article ist die Implementierung des Vertrags. Und &impl Summarize in notify akzeptiert jeden Typ, der den Vertrag erfüllt — Article heute, ein Tweet morgen.

    Trait-Bounds verbinden Generics mit Verhalten

    • fn notify(item: &impl Summarize) — kurzform für einen generischen Parameter
    • Bounds dokumentieren: was die Funktion wirklich vom Typ braucht
    • Default-Methoden machen Traits erweiterbar, ohne alle Implementierer zu brechen

    📝 Schnellprüfung

    Article implementiert nur author und title. Warum darf notify item.summary() aufrufen?

    Übung: der fehlende Vertrag

    🧩 Übung m8-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 8 — Traits & Generics

      Die Std-Traits, die jeder kennt

      Vieles vom Standardverhalten kommt aus Standard-Traits — meist per derive, manchmal von Hand:

      #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
      struct Money {
          euros: u32,
          cents: u8,
      }
      
      fn main() {
          let preis = Money { euros: 12, cents: 34 };
          let rabatt = Money { euros: 9, cents: 99 };
          println!("{preis:?}");
          println!("teurer: {}", preis > rabatt);
      }
      

      Was jeder einzelne bringt:

      Traitgibtper derive?
      Debug{:?}-Ausgabeja
      Clone / Copyduplizieren / kopieren beim Zuweisenja
      PartialEq / Eq== und !=ja
      PartialOrd / Ord<, >, sortierenja
      Default::default() mit Nullwertenja
      Hashals HashMap-Schlüsselja
      Display{}-Ausgabe für Nutzervon Hand

      Display zeigt, warum "von Hand": Nutzerausgabe will FORMAT — Währung, Dezimalstellen, Locale. Das kann kein derive erraten:

      use std::fmt;
      
      impl fmt::Display for Money {
          fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
              write!(f, "{},{:02} €", self.euros, self.cents)
          }
      }
      
      fn main() {
          let m = Money { euros: 12, cents: 4 };
          println!("{m}");
      }
      

      Und From/Into — das Modul-7-Brücke-Werkzeug: implementierst du From, bekommst du Into gratis dazu.

      📝 Schnellprüfung

      Warum kannst du Copy für Money ableiten, aber nicht für einen Struct mit String-Feld?

      Übung: Money komplett machen

      🧩 Übung m8-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 8 — Traits & Generics

        Mini-Projekt: stats-lib

        Zeit für ein Mini-Projekt mit Produktqualität: eine kleine Statistik- Bibliothek. Der Clou ist die API-Entscheidung: Mittelwert und Median geben ein Option zurück — denn eine leere Datenreihe hat keinen Mittelwert.

        🏢 Praxis-Szenario: Die Auswertung, die lügt

        Dein Team analysiert Messreihen. Ein Kollege schrieb eine mean-Funktion, die bei leerer Eingabe 0.0 zurückgibt. Folge: Dashboards zeigen plötzlich 'Durchschnitt 0 ms' — und niemand merkt, dass die Datenquelle leer war. Der Fehler saß nicht in der Berechnung, sondern in der API: sie hatte keinen Weg zu sagen 'kein Ergebnis'.

        🧩 Übung m8-l4-e1

        🔮 Vorhersage: Was passiert beim Ausführen?

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

        Was du in Modul 8 geschafft hast

        • Generics mit Bounds: eine Funktion statt vier Kopien — gratis durch Monomorphisierung
        • Eigene Traits mit Default-Methoden; &impl Trait als offener Parameter
        • Die Std-Traits: Debug/Clone/Copy/Vergleich per derive, Display/From von Hand
        • Eine API, die 'kein Ergebnis' als Wert ausdrückt — statt zu lügen

        ✅ Wichtige Erkenntnisse

        Haken setzen, um deinen Lernfortschritt zu markieren:

          Modul-Checkpoint

          Checkpoint: Modul 8

          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.