Generics in Rust: Typen sicher und flexibel nutzen lernen

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

Generics in Rust: Warum du sie unbedingt brauchst

\n\n

Wenn du anfängst, mit Rust zu programmieren, fühlt sich das Typsystem oft wie ein Feind an. Der Compiler meckert über jedes Komma und das Wort Generic lässt dich erzittern. Aber in Wahrheit sind Generics (generische Typen) dein bester Freund. Sie ermöglichen dir, flexiblen, sicheren und wiederverwendbaren Code zu schreiben – ohne Performance-Einbußen.

\n\n

In diesem Artikel tauchen wir tief in die Welt der Generics in Rust ein. Du lernst, wie du sie in Funktionen, Strukturen und Traits einsetzt. Du wirst sehen: Generische Programmierung ist keine Magie, sondern ein mächtiges Werkzeug, das deinen Code sauberer und schneller macht.

\n\n

Was sind Generics und warum braucht man sie?

\n\n

Stell dir vor, du schreibst eine Funktion, die das größte Element in einem Array findet. Ohne Generics müsstest du für jeden Datentyp (i32, f64, String usw.) eine separate Implementierung schreiben. Das ist zeitaufwendig, mühsam und verletzt das DRY-Prinzip (Don't Repeat Yourself).

\n\n

Generics lösen dieses Problem. Sie erlauben es, Typen zu parametrisieren: Du schreibst eine einzige Funktion, die mit jedem Datentyp funktioniert. Der Compiler generiert dann automatisch die passenden Code-Versionen für jeden konkreten Fall. Dieser Vorgang heißt Monomorphisierung.

\n\n

Syntax der Generics: Vom Einfachen zum Komplexen

\n\n

1. Generische Funktionen

\n\n

Beginnen wir mit einem einfachen Beispiel. Angenommen, wir wollen eine Funktion, die den übergebenen Wert einfach zurückgibt (Identitätsfunktion):

\n\n
fn identity<T>(value: T) -> T {\n    value\n}\n\nfn main() {\n    let n = identity(42);\n    let s = identity("hallo");\n    println!("{}, {}", n, s); // 42, hallo\n}
\n\n

Hier ist <T> der Typparameter. T kann jeder beliebige Typ sein: Zahl, String, Struktur. Beim Aufruf errät der Compiler selbstständig, welcher Typ gemeint ist. Das funktioniert nicht durch dynamische Dispatch (wie in Java oder Python), sondern durch die Generierung von spezialisiertem Code während der Kompilierung. Deshalb opfert Rust keine Performance.

\n\n

2. Typeinschränkungen (Trait Bounds)

\n\n

Was aber, wenn wir nicht nur einen Wert übergeben, sondern zum Beispiel zwei Zahlen vergleichen wollen? Ein einfaches <T> reicht nicht, weil nicht für alle Typen ein Vergleichsoperator definiert ist. Wir müssen angeben, dass T das Trait PartialOrd implementieren muss:

\n\n
fn max<T: PartialOrd>(a: T, b: T) -> T {\n    if a > b { a } else { b }\n}\n\nfn main() {\n    println!("{}", max(10, 20));        // 20\n    println!("{}", max(3.14, 2.71));    // 3.14\n}
\n\n

Die Schreibweise T: PartialOrd ist ein Trait Bound. Sie sagt dem Compiler: „Verwende nur Typen, für die eine Vergleichsordnung definiert ist.“ Versuche, zwei Strings an max zu übergeben – es funktioniert, weil String ebenfalls PartialOrd implementiert. Übergibst du jedoch eine Struktur ohne diese Implementierung, erhältst du einen Kompilierungsfehler.

\n\n

3. Mehrfacheinschränkungen

\n\n

Manchmal muss ein Typ gleich mehrere Traits erfüllen. Zum Beispiel soll er sowohl vergleichbar als auch klonbar sein:

\n\n
fn clone_and_max<T: PartialOrd + Clone>(a: T, b: T) -> T {\n    let a_cloned = a.clone();\n    let b_cloned = b.clone();\n    if a_cloned > b_cloned { a } else { b }\n}
\n\n

Die Syntax T: PartialOrd + Clone bedeutet, dass der Typ beide Traits implementieren muss. Das ist mächtig, aber übertreibe es nicht: Zu viele Einschränkungen verschlechtern die Lesbarkeit.

\n\n

Generics in Strukturen und Aufzählungen

\n\n

Generische Strukturen

\n\n

Generics funktionieren nicht nur mit Funktionen, sondern auch mit benutzerdefinierten Typen. Ein klassisches Beispiel ist die Struktur Point, die Koordinaten beliebigen Typs speichern kann:

\n\n
struct Point<T> {\n    x: T,\n    y: T,\n}\n\nimpl<T> Point<T> {\n    fn new(x:                

Blogs

Buchempfehlungen