Generische Methoden
Teil des Abschnitts Object Oriented Programming der Rust-Journey von Coddy — Lektion 25 von 61.
Ein generisches Struct kann jeden Typ aufnehmen, aber es ist ohne Methoden zur Interaktion mit seinen Daten nicht besonders nützlich. Um Methoden für ein generisches Struct zu definieren, benötigen Sie eine spezielle Syntax im impl-Block.
Der Schlüssel liegt darin, den generischen Parameter am impl selbst zu deklarieren:
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn get(&self) -> &T {
&self.value
}
}
Beachten Sie das impl<T> vor Wrapper<T>. Dies teilt Rust mit, dass T ein generischer Typparameter für den gesamten Implementierungsblock ist. Ohne diese Deklaration würde Rust nach einem konkreten Typ namens T suchen und diesen nicht finden.
Die get-Methode gibt &T zurück – eine Referenz auf den Typ, den der Wrapper enthält. Dies funktioniert unabhängig davon, ob T ein Integer, ein String oder ein beliebiger anderer Typ ist:
let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };
println!("{}", num_wrapper.get()); // 100
println!("{}", text_wrapper.get()); // Rust
Dieselbe Methodendefinition funktioniert für beide, da sich der generische T zur Kompilierzeit an jeden konkreten Typ anpasst.
Aufgabe
EinfachLass uns deinen generischen Container um Methoden erweitern! Du wirst eine Box-Struktur bauen (nicht zu verwechseln mit Rusts Standard-Box), die jeden Typ aufnehmen kann und Methoden zur Interaktion mit ihrem Inhalt bereitstellt.
Du wirst deinen Code auf zwei Dateien aufteilen:
mybox.rs: Definiere eine öffentliche generische Struktur namensMyBox<T>mit einem privaten Feldcontentsvom TypT. Implementiere Methoden für diese Struktur:- Eine assoziierte Funktion
new, die eine neueMyBoxmit dem gegebenen Wert erstellt - Eine
peek-Methode, die eine Referenz auf den Inhalt zurückgibt (unter Verwendung von&self) - Eine
replace-Methode, die einen neuen Wert entgegennimmt und den aktuellen Inhalt ersetzt (unter Verwendung von&mut self)
- Eine assoziierte Funktion
main.rs: Binde dein Modul ein und demonstriere die generischen Methoden, die mit verschiedenen Typen arbeiten. Du wirst Boxen erstellen, einen Blick auf ihren Inhalt werfen und Werte ersetzen, um die Methoden in Aktion zu zeigen.
Erinnere dich an die wichtige Syntax für die Implementierung von Methoden an einer generischen Struktur: Du benötigst impl<T> vor MyBox<T>, um Rust mitzuteilen, dass T ein generischer Parameter für den gesamten Implementierungsblock ist.
Demonstriere in deiner Hauptdatei deine MyBox durch:
- Erstellen einer Box mit einer Ganzzahl (erste Eingabe, geparst als
i32) - Hineinschauen (Peeking) in den Inhalt und Ausgeben des Wertes
- Ersetzen des Inhalts durch eine neue Ganzzahl (zweite Eingabe, geparst als
i32) - Erneutes Hineinschauen, um den aktualisierten Wert zu zeigen
- Erstellen einer zweiten Box mit einem String (dritte Eingabe)
- Hineinschauen in den Inhalt der String-Box
Deine Ausgabe sollte folgendem Format entsprechen:
Integer box contains: {value}
After replace: {value}
String box contains: {value}Zum Beispiel mit den Eingaben 10, 25 und Rust:
Integer box contains: 10
After replace: 25
String box contains: RustDu wirst drei Eingaben erhalten: eine initiale Ganzzahl, eine Ersatz-Ganzzahl und einen String-Wert.
Spickzettel
Um Methoden für ein generisches Struct zu definieren, deklarieren Sie den generischen Parameter im impl-Block:
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn get(&self) -> &T {
&self.value
}
}
Die impl<T>-Syntax teilt Rust mit, dass T ein generischer Typparameter für den gesamten Implementierungsblock ist. Ohne dies würde Rust nach einem konkreten Typ namens T suchen.
Methoden können Referenzen auf den generischen Typ (&T) zurückgeben, was für jeden konkreten Typ funktioniert:
let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };
println!("{}", num_wrapper.get()); // 100
println!("{}", text_wrapper.get()); // Rust
Probier es selbst
mod mybox;
use mybox::MyBox;
fn main() {
// Eingaben lesen
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let initial_int: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let replacement_int: i32 = input2.trim().parse().expect("Invalid integer");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let string_value = input3.trim().to_string();
// TODO: Erstelle eine MyBox mit dem initialen Integer
// TODO: Wirf einen Blick auf den Inhalt und gib aus: "Integer box contains: {value}"
// TODO: Ersetze den Inhalt durch den replacement_int
// TODO: Wirf erneut einen Blick darauf und gib aus: "After replace: {value}"
// TODO: Erstelle eine zweite MyBox mit dem string_value
// TODO: Wirf einen Blick auf die String-Box und gib aus: "String box contains: {value}"
}
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