Generics in Rust: Wozu sie dienen und wie man sie verwendet

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Was sind Generics und warum braucht man sie in Rust?



Rust ist eine Systemprogrammiersprache, die für ihre Sicherheit, Leistung und ihr strenges Typsystem bekannt ist. Eines der wichtigsten Werkzeuge, um flexiblen und wiederverwendbaren Code zu schreiben, sind Generics (generische Typen).



Generics ermöglichen es, Funktionen, Strukturen, Aufzählungen und Methoden zu erstellen, die mit verschiedenen Datentypen arbeiten können, ohne Code duplizieren zu müssen. Anstatt eine separate Funktion für i32, f64 oder String zu schreiben, schreiben Sie eine einzige generische Version.



Zum Beispiel müssten Sie ohne Generics drei verschiedene Funktionen zum Sortieren von drei verschiedenen Typen erstellen. Mit Generics nur eine. Das reduziert nicht nur die Code-Menge, sondern erhöht auch die Zuverlässigkeit: Der Compiler überprüft die Typkorrektheit zur Kompilierzeit, nicht zur Laufzeit.



Hauptteil: Wie Generics in Rust funktionieren



1. Generische Funktionen: Einmal schreiben – für beliebige Typen verwenden



Der einfachste Weg, Generics kennenzulernen, sind generische Funktionen. Stellen Sie sich vor, Sie müssen eine Funktion schreiben, die den größten von zwei Werten zurückgibt. Anstatt sie für jeden Typ separat zu schreiben, verwenden wir eine Verallgemeinerung:



fn largest<T: PartialOrd>(list: &[T]) -> &T {    let mut largest = &list[0];    for item in list {        if item > largest {            largest = item;        }    }    largest}

fn main() { let numbers = vec![34, 50, 25, 100, 65]; let result = largest(&numbers); println!("Größte Zahl: {}", result);

let chars = vec!['y', 'm', 'a', 'q']; let result = largest(&chars); println!("Größtes Zeichen: {}", result);}


Hier bedeutet <T: PartialOrd>, dass die Funktion mit jedem Typ T arbeitet, der Vergleiche unterstützt (das Trait PartialOrd). Dies ist eine Trait-Beschränkung – ein wichtiges Konzept, das später behandelt wird.



2. Generische Strukturen und Aufzählungen: Flexibilität bei der Datendefinition



Generics sind nicht nur auf Funktionen anwendbar, sondern auch auf Datenstrukturen. Ein klassisches Beispiel ist die Struktur Point, die Koordinaten verschiedener Typen speichern kann:



struct Point<T> {    x: T,    y: T,}

fn main() { let integer_point = Point { x: 5, y: 10 }; let float_point = Point { x: 1.0, y: 4.0 }; // let wont_work = Point { x: 5, y: 4.0 }; // Fehler! Unterschiedliche Typen}


Wenn x und y unterschiedliche Typen haben können sollen, verwenden Sie mehrere generische Parameter:



struct Point<T, U> {    x: T,    y: U,}

fn main() { let mixed_point = Point { x: 5, y: 4.0 }; // Funktioniert!}


Dasselbe gilt für Aufzählungen. Die bekanntesten Beispiele sind Option<T> und Result<T, E>, die generisch sind:



enum Option<T> {    Some(T),    None,}

enum Result<T, E> { Ok(T), Err(E),}


3. Methoden in generischen Strukturen: Logik hinzufügen



Sie können Methoden für generische Strukturen definieren, indem Sie einen impl-Block verwenden. Dabei können Sie Methoden erstellen, die nur für bestimmte Typen funktionieren:



struct Point<T> {    x: T,    y: T,}

// Methoden für alle Typen Timpl<T> Point<T> { fn x(&self) -> &T { &self.x }}

// Methode nur für den Typ f64impl Point<f64> { fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() }}

fn main() { let p = Point { x: 3.0, y: 4.0 }; println!("p.x = {}", p.x()); println!("Abstand zum Ursprung: {}", p.distance_

Blogs

Buchempfehlungen