Standardimplementierungen
Teil des Abschnitts Objektorientierte Programmierung der Rust-Journey von Coddy. Lektion 31 von 61.
Bisher war jede Trait-Methode, die wir definiert haben, lediglich eine Signatur: eine Anforderung ohne Rumpf. Aber was ist, wenn die meisten Typen, die ein Trait implementieren, dieselbe Logik verwenden würden? Rust ermöglicht es dir, direkt in der Trait-Definition eine Standardimplementierung bereitzustellen.
Statt die Methodensignatur mit einem Semikolon zu beenden, fügst du einen Methodenkörper hinzu:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
Jetzt erhält jeder Typ, der Greet implementiert, automatisch diese Methode, ohne Code im impl-Block schreiben zu müssen:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // Hallo!
Das leere impl Greet for Guest {} ist gültig, weil das Trait das Verhalten bereits bereitstellt. Die Struktur „entscheidet sich einfach“ für das Trait und erhält die Standardmethode kostenlos.
Dieses Muster ist nützlich, wenn du gemeinsames Verhalten definieren möchtest, das für die meisten Fälle funktioniert. Typen, die etwas anderes benötigen, können den default überschreiben (das lernst du als Nächstes), während Typen, die mit dem Standardverhalten zufrieden sind, sich nicht wiederholen müssen.
Aufgabe
EinfachLass uns die Macht von Standardimplementierungen erkunden, indem wir ein Benachrichtigungssystem erstellen! Du wirst ein Trait erstellen, das standardmäßig sofort verfügbares Verhalten bereitstellt, sodass Typen es übernehmen können, ohne sich wiederholenden Code schreiben zu müssen.
Du wirst deinen Code auf drei Dateien verteilen:
notifiable.rs: Definiere ein öffentlichesNotifiable-Trait mit einernotify(&self) -> String-Methode, die eine Standardimplementierung besitzt. Die Standardimplementierung sollte"You have a new notification!"zurückgeben. Jeder Typ, der dieses Trait implementiert, erhält dieses Verhalten automatisch, ohne dass du etwas in seinem impl-Block schreiben musst.alerts.rs: Erstelle zwei einfache öffentliche Unit-Strukturen, die dein Trait implementieren:EmailAlert: implementiertNotifiablemit einem leeren impl-Block (verwendet die Standardimplementierung)SystemAlert: implementiert ebenfallsNotifiablemit einem leeren impl-Block (verwendet die Standardimplementierung)
main.rs: Führe deine Module zusammen und zeige, wie beide Alarmtypen dasselbe Standardverhalten verwenden. Erstelle Instanzen beider Strukturen und rufe ihrenotify-Methoden auf.
Erstelle in deiner Hauptdatei ein EmailAlert und ein SystemAlert und gib anschließend ihre Benachrichtigungen in separaten Zeilen aus.
Deine Ausgabe sollte wie folgt aussehen:
You have a new notification!
You have a new notification!Beachte, dass beide Typen eine identische Ausgabe erzeugen: Das ist die Stärke von Standardimplementierungen! Keine der beiden Strukturen musste Code für die notify-Methode schreiben, und trotzdem verfügen beide über ein vollständig funktionsfähiges Benachrichtigungsverhalten. In der nächsten Lektion lernst du, wie du diese Standardimplementierungen überschreibst, wenn ein Typ ein benutzerdefiniertes Verhalten benötigt.
Probier es selbst
mod notifiable;
mod alerts;
use notifiable::Notifiable;
use alerts::{EmailAlert, SystemAlert};
fn main() {
// TODO: Erstelle eine Instanz von EmailAlert
// TODO: Erstelle eine Instanz von SystemAlert
// TODO: Print the notification from EmailAlert
// TODO: Print the notification from SystemAlert
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Methoden und Verhalten
Einführung in ImplementierungsblöckeDer Self-ParameterVeränderliche MethodenAssoziierte FunktionenMehrere ImplementierungsblöckeMethod ChainingRückblick – Rechteck-Aktionen4Projekt: Virtuelles Haustier
Das Haustier definierenDas Haustier füttern7Standard-Traits
Das Debug-TraitDas Display-TraitClone und CopyGleichheits-TraitsZusammenfassung – Druckbarer Punkt10Projekt: Dokumentensystem
Das Draw-TraitText-Komponente2Kapselung und Module
Grundlagen der ModuleDas Public-SchlüsselwortPrivate FelderGetterSetterWiederholung – Sicherer Tresor5Generics
Generische StructsGenerische MethodenMehrere generische TypenGenerische FunktionenZusammenfassung - Koordinatenpunkt8Traits als Einschränkungen
Syntax für Trait BoundsMehrfache BoundsDie Where-KlauselTypen mit Traits zurückgebenRückblick – Generischer Drucker11Design Patterns in Rust
Newtype-PatternKompositionDas Drop-TraitFrom und IntoZusammenfassung – Smart Pointer Mock3Fortgeschrittene Enums
Enums mit DatenMethoden für EnumsMatching von DatenvariantenDas Option-Enum erneut betrachtetZusammenfassung – Shape-Enum6Definition von Traits
Was ist ein Trait?Traits implementierenStandardimplementierungenStandardimplementierungen überschreibenTraits mit ParameternZusammenfassung – Media PlayerÜbe selbstständig: Online-Rust-Compiler