Traits in Rust sind eine Möglichkeit, ein Interface zu definieren: Sie beschreiben eine Menge von Methoden, die ein Typ implementieren muss. Die Implementierung eines Traits ermöglicht die Verwendung von Generika und dynamischen Methodendispatch.
Statische Dispatchierung (durch Generika und Bound):
Die konkrete Implementierung einer Methode wird zur Compile-Zeit aufgerufen. Es wird generischer Code verwendet (impl<T: Trait> oder fn foo<T: Trait>(t: T)), und für jeden Typ wird zur Compile-Zeit eine separate Version der Funktion erstellt (Monomorphisierung).
Dynamische Dispatchierung:
Wird verwendet, wenn eine Funktion Objekte als Referenzen auf einen Trait (&dyn Trait) entgegennimmt; der Compiler weiß nicht im Voraus, welcher Typ verwendet wird. Der Methodenaufruf erfolgt zur Laufzeit über eine vtable (virtuelle Tabelle).
Beispiel:
trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // Statische Dispatchierung def print_animal_static<T: Animal>(a: T) { a.speak(); } // Dynamische Dispatchierung def print_animal_dyn(a: &dyn Animal) { a.speak(); }
Wann verwenden:
Kann man Werte mit generischem Trait-Typ in Vec<Trait> speichern? Warum?
Antwort:
Nein, das ist nicht möglich. Vec benötigt Sized-Typen, und Traits haben an sich keine Größe. Es muss Box<dyn Trait> oder Referenzen auf Traits verwendet werden.
// let v: Vec<Trait> = vec![]; // Kompilierungsfehler let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // korrekt
Geschichte
Der Versuch, eine Sammlung von Trait-Objekten ohne Verwendung von Box oder Rc zu erstellen:
// let coll: Vec<MyTrait> = vec![]; // Fehler: Die Größe für Werte des Typs ... kann nicht bekannt sein
Geschichte
Geschichte
Im Demoprojekt versuchten sie, Referenzen auf temporäre Objekte (&dyn Trait) zu speichern, um mit verschiedenen Protokollen zu interagieren, was zu dangling references und Laufzeit-Panik führte. Sie mussten auf Box<dyn Trait> mit explizitem Besitz umsteigen.