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 jedem Typ funktionieren. Das ist nützlich, wenn du eine Hilfsfunktion benötigst, die nicht zu einem bestimmten struct gehört, aber dennoch flexibel sein sollte.
Die Syntax entspricht dem, was du bereits bei Strukturen 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 eines beliebigen Typs entgegen und gibt ihn unverändert zurück. <T> deklariert den generischen Typ, value: T akzeptiert ihn als Parameter, 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 in Funktionen auch mehrere generische Parameter verwenden, genau wie bei Strukturen:
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. Dadurch wird doppelter Code reduziert und gleichzeitig die Typsicherheit beibehalten.
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 über 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 potenziell unterschiedlicher Typen entgegen und gibt sie in umgekehrter Reihenfolge als Tupel(U, T)zurück
main.rs: Importiere dein Hilfsmodul und demonstriere diese generischen Funktionen mit verschiedenen Typen. Zeige, wie dieselben Funktionsdefinitionen nahtlos Ganzzahlen, Gleitkommazahlen und Zeichenketten verarbeiten.
Demonstriere in deiner Hauptdatei deine Hilfsfunktionen, indem du:
wrap_in_pairmit einer Ganzzahl (erste Eingabe, alsi32geparst) verwendest und beide Elemente ausgibstwrap_in_pairmit einer Zeichenkette (zweite Eingabe) verwendest und beide Elemente ausgibstswapmit einer Ganzzahl (dritte Eingabe, alsi32geparst) und einer Zeichenkette (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 für Ganzzahlen und Zeichenketten identisch funktioniert und swap zwei völlig unterschiedliche Typen verarbeitet: Das ist die Flexibilität generischer Funktionen!
Du erhältst vier Eingaben: eine Ganzzahl, eine Zeichenkette, eine weitere Ganzzahl und eine weitere Zeichenkette.
Probier es selbst
mod utils;
use std::io;
fn main() {
// Lese die vier Eingaben
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, dann gib 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