Generische Funktionen
Teil des Abschnitts Objektorientierte Programmierung der Rust-Journey von Coddy. Lektion 27 von 61.
Generics sind nicht auf Structs beschränkt. Du kannst auch eigenständige Funktionen schreiben, die mit beliebigen Typen arbeiten. Das ist nützlich, wenn du eine Hilfsfunktion brauchst, die nicht zu einem bestimmten Struct gehört, aber trotzdem flexibel sein soll.
Die Syntax entspricht dem, was du bei Structs gesehen hast. Deklariere den generischen Parameter in spitzen Klammern nach dem Funktionsnamen und verwende ihn dann in den Parametern und im Rückgabetyp:
fn identity<T>(value: T) -> T {
value
}
Diese Funktion identity nimmt einen Wert beliebigen Typs entgegen und gibt ihn unverändert zurück. <T> deklariert den generischen Typ, value: T nimmt ihn als Parameter entgegen, und -> T legt den Rückgabetyp fest. Wenn du die Funktion aufrufst, leitet Rust den konkreten Typ ab:
let num = identity(42); // T ist i32
let text = identity("hello"); // T ist &str
Du kannst auch mehrere generische Parameter in Funktionen verwenden, genau wie bei Structs:
fn make_pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
let pair = make_pair(10, "ten"); // gibt (i32, &str) zurück
Generische Funktionen ermöglichen es dir, wiederverwendbare Logik einmal zu schreiben und auf viele Typen anzuwenden. So reduzierst du doppelten Code und erhältst gleichzeitig die Typsicherheit.
Aufgabe
EinfachErstellen wir ein Hilfsmodul mit generischen Funktionen, die mit jedem Typ arbeiten können! Du erstellst eigenständige Funktionen, die zeigen, wie Generics deinen Code flexibel und wiederverwendbar machen, ohne ihn an eine bestimmte Struktur zu binden.
Du organisierst deinen Code auf zwei Dateien:
utils.rs: Erstelle eine Sammlung öffentlicher generischer Hilfsfunktionen:wrap_in_pair<T>: nimmt einen einzelnen Wert entgegen und gibt ein Tupel zurück, das diesen Wert zweimal enthält:(value, value). Dafür muss der TypCloneimplementieren, verwende also<T: Clone>swap<T, U>: nimmt zwei Werte an, die unterschiedliche Typen haben können, und gibt sie in umgekehrter Reihenfolge als Tupel(U, T)zurück
main.rs: Binde dein Hilfsmodul ein und zeige, wie diese generischen Funktionen mit verschiedenen Typen funktionieren. Zeige, wie dieselben Funktionsdefinitionen nahtlos mit Ganzzahlen, Gleitkommazahlen und Zeichenfolgen umgehen.
Zeige in deiner Hauptdatei deine Hilfsfunktionen, indem du:
wrap_in_pairmit einer Ganzzahl (erste Eingabe, alsi32geparst) verwendest und beide Elemente ausgibstwrap_in_pairmit einer Zeichenfolge (zweite Eingabe) verwendest und beide Elemente ausgibstswapmit einer Ganzzahl (dritte Eingabe, alsi32geparst) und einer Zeichenfolge (vierte Eingabe) verwendest und anschließend das vertauschte Ergebnis ausgibst
Deine Ausgabe sollte diesem Format entsprechen:
Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})Zum Beispiel mit den Eingaben 5, hello, 42 und world:
Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)Beachte, wie wrap_in_pair bei Ganzzahlen und Zeichenfolgen identisch funktioniert und swap mit zwei völlig unterschiedlichen Typen umgehen kann: Das ist die Flexibilität generischer Funktionen!
Du erhältst vier Eingaben: eine Ganzzahl, eine Zeichenfolge, eine weitere Ganzzahl und eine weitere Zeichenfolge.
Probier es selbst
mod utils;
use std::io;
fn main() {
// Lies die vier Eingaben ein
let mut input1 = String::new();
io::stdin().read_line(&mut input1).expect("Failed to read line");
let num1: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
io::stdin().read_line(&mut input2).expect("Failed to read line");
let str1 = input2.trim().to_string();
let mut input3 = String::new();
io::stdin().read_line(&mut input3).expect("Failed to read line");
let num2: i32 = input3.trim().parse().expect("Invalid integer");
let mut input4 = String::new();
io::stdin().read_line(&mut input4).expect("Failed to read line");
let str2 = input4.trim().to_string();
// TODO: Verwende wrap_in_pair mit num1 und gib das Ergebnis aus
// Format: Pair of ints: ({value}, {value})
// TODO: Verwende wrap_in_pair mit str1 und gib das Ergebnis aus
// Format: Pair of strings: ({value}, {value})
// TODO: Verwende swap mit num2 und str2 und gib anschließend das vertauschte Ergebnis aus
// Format: Swapped: ({string}, {int})
}
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