Standardimplementierungen
Teil des Abschnitts Object Oriented Programming der Rust-Journey von Coddy — Lektion 31 von 61.
Bisher war jede Trait-Methode, die wir definiert haben, nur eine Signatur – eine Anforderung ohne Rumpf. Aber was ist, wenn die meisten Typen, die einen Trait implementieren, dieselbe Logik verwenden würden? Rust ermöglicht es dir, eine Standardimplementierung direkt in der Trait-Definition bereitzustellen.
Anstatt die Methodensignatur mit einem Semikolon zu beenden, fügen Sie einen Rumpf hinzu:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
Jetzt erhält jeder Typ, der Greet implementiert, automatisch diese Methode – ohne dass Code im impl-Block geschrieben werden muss:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // Hallo!
Das leere impl Greet for Guest {} ist gültig, da der Trait bereits das Verhalten bereitstellt. Die Struct entscheidet sich einfach für den Trait und erhält die Standardmethode kostenlos.
Dieses Muster ist nützlich, wenn Sie gemeinsames Verhalten definieren möchten, das in den meisten Fällen funktioniert. Typen, die etwas anderes benötigen, können den Standard überschreiben (was Sie als Nächstes lernen werden), während Typen, die mit dem Standardverhalten zufrieden sind, sich nicht wiederholen müssen.
Aufgabe
EinfachLassen Sie uns die Stärke von Standardimplementierungen erkunden, indem wir ein Benachrichtigungssystem bauen! Sie werden ein Trait erstellen, das standardmäßiges Verhalten direkt mitliefert, sodass Typen dieses nutzen können, ohne repetitiven Code schreiben zu müssen.
Sie werden Ihren Code über drei Dateien organisieren:
notifiable.rs: Definieren Sie ein öffentlichesNotifiable-Trait mit einernotify(&self) -> String-Methode, die eine Standardimplementierung besitzt. Der Standardwert sollte"You have a new notification!"zurückgeben. Jeder Typ, der dieses Trait implementiert, erhält automatisch dieses Verhalten, ohne etwas in seinen impl-Block schreiben zu müssen.alerts.rs: Erstellen Sie zwei einfache öffentliche Unit-Structs, die Ihr Trait implementieren:EmailAlert— implementiertNotifiablemit einem leeren impl-Block (verwendet den Standard)SystemAlert— implementiert ebenfallsNotifiablemit einem leeren impl-Block (verwendet den Standard)
main.rs: Führen Sie Ihre Module zusammen und demonstrieren Sie, wie beide Alert-Typen dasselbe Standardverhalten teilen. Erstellen Sie Instanzen beider Structs und rufen Sie derennotify-Methoden auf.
Erstellen Sie in Ihrer Hauptdatei ein EmailAlert und ein SystemAlert und geben Sie deren Benachrichtigungen in separaten Zeilen aus.
Ihre Ausgabe sollte wie folgt aussehen:
You have a new notification!
You have a new notification!Beachten Sie, dass beide Typen die identische Ausgabe erzeugen – das ist das Schöne an Standardimplementierungen! Keine der Structs musste Code für die notify-Methode schreiben, und dennoch verfügen beide über ein voll funktionsfähiges Benachrichtigungsverhalten. In der nächsten Lektion lernen Sie, wie Sie diese Standards überschreiben können, wenn ein Typ ein benutzerdefiniertes Verhalten benötigt.
Spickzettel
Trait-Methoden können Standardimplementierungen haben, indem ein Methodenkörper anstelle von nur einer Signatur bereitgestellt wird:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
Typen, die das Trait implementieren, erhalten automatisch das Standardverhalten, ohne dass Code geschrieben werden muss:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // Hello there!
Ein leerer impl-Block ist gültig, wenn das Trait Standardimplementierungen bereitstellt. Dies ermöglicht es Typen, sich für gemeinsames Verhalten zu entscheiden, ohne Wiederholungen zu erzeugen.
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: Gib die Benachrichtigung von EmailAlert aus
// TODO: Gib die Benachrichtigung von SystemAlert aus
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Object Oriented Programming
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