Generische Structs
Teil des Abschnitts Objektorientierte Programmierung der Rust-Journey von Coddy. Lektion 24 von 61.
Bisher hatte jede von dir erstellte Struktur Felder mit bestimmten, konkreten Typen. Ein Pet hat einen String-Namen, ein Rectangle hat f64-Dimensionen. Aber was, wenn du eine Struktur erstellen möchtest, die beliebige Datentypen enthalten kann?
Hier kommen Generics ins Spiel. Mit Generics kannst du flexiblen, wiederverwendbaren Code schreiben, der mit mehreren Typen funktioniert. Anstatt einen bestimmten Typ fest einzucodieren, verwendest du einen Platzhalter, typischerweise T, der durch einen konkreten Typ ersetzt wird, wenn du die Struktur verwendest.
Hier ist eine einfache Wrapper-Struktur, die jeden Typ aufnehmen kann:
struct Wrapper<T> {
value: T,
}
Das <T> nach dem Namen der Struktur deklariert einen generischen Typparameter. Innerhalb der Struktur dient T als Platzhalter für den Typ, den du tatsächlich verwenden wirst. Wenn du eine Instanz erstellst, leitet Rust den konkreten Typ ab:
let int_wrapper = Wrapper { value: 42 }; // T ist i32
let float_wrapper = Wrapper { value: 3.14 }; // T ist f64
let text_wrapper = Wrapper { value: "hello" }; // T ist &str
Jedes davon ist ein anderer konkreter Typ: Wrapper<i32>, Wrapper<f64> und Wrapper<&str>, aber sie verwenden alle dieselbe Struct-Definition. Dadurch entfällt die Notwendigkeit, separate Structs wie IntWrapper, FloatWrapper und StringWrapper zu schreiben, die im Wesentlichen dasselbe tun.
Der Buchstabe T ist lediglich eine Konvention (kurz für „Typ“). Du könntest jeden gültigen Bezeichner verwenden, aber T ist in Rust und den meisten anderen Sprachen mit Generics der Standard.
Aufgabe
EinfachLass uns einen generischen Container erstellen, der jeden Werttyp aufnehmen kann! Du erstellst eine Container-Struktur, die zeigt, wie Generics deinen Code flexibel und wiederverwendbar machen.
Du wirst deinen Code auf zwei Dateien aufteilen:
container.rs: Definiere eine öffentliche generische Struktur namensContainer<T>mit einem einzigen öffentlichen Felditemvom TypT. Dieser Container sollte jeden Typ aufnehmen können: Ganzzahlen, Fließkommazahlen, Zeichenketten oder alles andere.main.rs: Binde dein Container-Modul ein und demonstriere seine Flexibilität, indem du Container erstellst, die verschiedene Datentypen aufnehmen. Du wirst drei Container erstellen und anzeigen, was jeder von ihnen enthält.
Erstelle in deiner Hauptdatei die folgenden Container:
- Einen Container, der einen Ganzzahlwert enthält (die erste Eingabe, geparst als
i32) - Einen Container, der einen Fließkommawert enthält (die zweite Eingabe, geparst als
f64) - Einen Container, der eine Zeichenkette enthält (die dritte Eingabe, als
Stringbeibehalten)
Deine Ausgabe sollte den Inhalt jedes Containers in diesem Format anzeigen:
Integer container: {value}
Float container: {value}
String container: {value}Zum Beispiel bei den Eingaben 42, 3.14 und hello:
Integer container: 42
Float container: 3.14
String container: helloBeachte, wie dieselbe Definition von Container<T> für alle drei verschiedenen Typen funktioniert: Das ist die Stärke von Generics!
Du erhältst drei Eingaben: einen Ganzzahlwert, einen Fließkommawert und einen Zeichenkettenwert.
Probier es selbst
mod container;
use container::Container;
fn main() {
// Lies die drei Eingaben
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let int_value: i32 = input1.trim().parse().expect("Failed to parse integer");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let float_value: f64 = input2.trim().parse().expect("Failed to parse float");
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: Create three containers using Container<T>
// 1. An integer container holding int_value
// 2. A float container holding float_value
// 3. A string container holding string_value
// TODO: Gib den Inhalt jedes Containers im erforderlichen Format aus
// Integer container: {value}
// Float container: {value}
// String container: {value}
}
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