Tupel in Rust
Ein Tupel fasst mehrere Werte zu einem einzigen zusammen – und diese Werte dürfen unterschiedliche Typen haben. So bündelst du schnell zusammengehörige Dinge, ohne gleich einen eigenen Typ dafür zu definieren.
Ein Tupel erstellen
Abschnitt betitelt „Ein Tupel erstellen“fn main() { let person: (&str, i32, bool) = ("Alice", 30, true); println!("{:?}", person);}Ausgabe:
("Alice", 30, true)Das Tupel mischt hier einen &str, einen i32 und einen bool in einem Wert. Ein Array kann das nicht: Dort müssen alle Elemente denselben Typ haben.
Um ein ganzes Tupel auszugeben, nimmst du {:?} (Debug-Formatierung) statt {}. Ein einfaches {} funktioniert bei Tupeln nicht.
Per Position auf Elemente zugreifen
Abschnitt betitelt „Per Position auf Elemente zugreifen“Mit .0, .1, .2 und so weiter greifst du auf die Elemente zu. Die Zählung beginnt bei null:
fn main() { let person = ("Alice", 30, true);
println!("Name: {}", person.0); println!("Alter: {}", person.1); println!("Aktiv: {}", person.2);}Ausgabe:
Name: AliceAlter: 30Aktiv: trueDie Position muss eine feste Zahl sein, die direkt im Code steht. Eine Variable wie in person.i geht nicht: Jede Position kann einen anderen Typ haben, und Rust muss den Typ schon beim Kompilieren kennen.
Ein Tupel destrukturieren
Abschnitt betitelt „Ein Tupel destrukturieren“Du kannst alle Werte in einer einzigen Zeile in eigene Variablen auspacken. Das nennt man Destrukturieren:
fn main() { let person = ("Alice", 30, true); let (name, age, is_active) = person;
println!("{name} ist {age}, aktiv: {is_active}");}Ausgabe:
Alice ist 30, aktiv: trueDas ist oft lesbarer als ständig .0, .1, .2 zu schreiben – vor allem, wenn ein Tupel mehr als zwei oder drei Felder hat.
Mehrere Werte aus einer Funktion zurückgeben
Abschnitt betitelt „Mehrere Werte aus einer Funktion zurückgeben“Eine Funktion kann nur einen Rückgabetyp angeben. Willst du mehr als einen Wert zurückgeben, ist ein Tupel der natürliche Weg:
fn min_max(numbers: &[i32]) -> (i32, i32) { let min = *numbers.iter().min().unwrap(); let max = *numbers.iter().max().unwrap(); (min, max)}
fn main() { let (smallest, largest) = min_max(&[4, 8, 1, 9, 3]); println!("Minimum: {smallest}, Maximum: {largest}");}Ausgabe:
Minimum: 1, Maximum: 9Mach dir um &[i32], .iter() und .unwrap() noch keine Gedanken. Die kommen auf den Seiten Slices, Iteratoren und Option dran. Wichtig ist hier der Rückgabetyp (i32, i32) – und wie main ihn sofort in zwei Variablen destrukturiert.
Tupel ändern und Werte tauschen
Abschnitt betitelt „Tupel ändern und Werte tauschen“Ist ein Tupel mit mut deklariert, kannst du seine Felder einzeln ändern – über dieselben Positionen .0, .1. Die Werte dürfen sich ändern, die Form aber nicht: Ein Tupel behält immer die Anzahl und die Typen der Felder, mit denen es angefangen hat.
Destrukturieren funktioniert auch andersherum: Du kannst in bestehende Variablen zuweisen. So tauschst du zwei Werte elegant in einer Zeile, ganz ohne Hilfsvariable. Brauchst du nur einige der Felder, schreib _ für die, die dich nicht interessieren:
fn main() { let mut point = (3, 4); point.0 += 10; // ein Feld direkt ändern println!("point ist {:?}", point);
let mut a = 1; let mut b = 2; (a, b) = (b, a); // die beiden Werte tauschen println!("a = {a}, b = {b}");
let (x, _) = point; // x behalten, das zweite Feld ignorieren println!("x = {x}");}Ausgabe:
point ist (13, 4)a = 2, b = 1x = 13Der Tausch klappt, weil Rust zuerst die rechte Seite (b, a) baut. Erst danach packt es sie in a und b aus. Was du mit _ und Mustern sonst noch alles machen kannst, zeigt dir die Seite Pattern Matching.
Das leere Tupel: ()
Abschnitt betitelt „Das leere Tupel: ()“Ein Tupel ist dir wahrscheinlich schon begegnet, ohne dass du es gemerkt hast. () heißt Unit-Typ. Diesen Wert gibt eine Funktion zurück, wenn sie nichts Sinnvolles zurückgibt – er ist zum Beispiel der unsichtbare Rückgabetyp von fn main().
Tupel oder Struct?
Abschnitt betitelt „Tupel oder Struct?“Tupel sind ideal für schnelle Gruppierungen ohne Namen – besonders, wenn eine Funktion zwei oder drei Werte zurückgibt. Sobald eine Gruppe von Werten aber ein echtes Konzept mit benannten Feldern darstellt (etwa eine Person mit Name und Alter, auf die du im ganzen Code über die Namen zugreifst), ist ein Struct meist die bessere und lesbarere Wahl.
Übungen
Abschnitt betitelt „Übungen“1. Summe und Produkt
Abschnitt betitelt „1. Summe und Produkt“Schreib eine Funktion sum_and_product(a: i32, b: i32) -> (i32, i32), die sowohl die Summe als auch das Produkt zweier Zahlen zurückgibt. Ruf sie in main mit 5 und 6 auf und destrukturiere das Ergebnis. Tipp: Die letzte Zeile der Funktion ist das Tupel selbst, (a + b, a * b), ohne Semikolon.
Lösung anzeigen
fn sum_and_product(a: i32, b: i32) -> (i32, i32) { (a + b, a * b)}
fn main() { let (sum, product) = sum_and_product(5, 6); println!("Summe = {sum}, Produkt = {product}");}Ausgabe:
Summe = 11, Produkt = 302. Ein Rechteck aus zwei Ecken
Abschnitt betitelt „2. Ein Rechteck aus zwei Ecken“Ein Rechteck lässt sich als Tupel aus zwei Punkten speichern, wobei jeder Punkt selbst ein Tupel ist: let rect = ((1, 2), (5, 5));. Berechne Breite, Höhe und Fläche. Tipp: Tupel lassen sich verschachteln, und rect.1.0 bedeutet „das erste Feld des zweiten Punkts“.
Lösung anzeigen
fn main() { let rect = ((1, 2), (5, 5));
let width = rect.1.0 - rect.0.0; let height = rect.1.1 - rect.0.1;
println!("Breite {width}, Höhe {height}, Fläche {}", width * height);}Ausgabe:
Breite 4, Höhe 3, Fläche 12Du kannst auch beide Punkte auf einmal destrukturieren: let ((x1, y1), (x2, y2)) = rect;. Das ist mehr Tipparbeit, aber x2 - x1 liest sich leichter als rect.1.0 - rect.0.0. Wenn sich die Punkte so stapeln, ist das ein Zeichen, dass ein Struct mit benannten Feldern klarer wäre.
Zusammenfassung
Abschnitt betitelt „Zusammenfassung“- Ein Tupel bündelt Werte, die unterschiedliche Typen haben dürfen:
(name, age, is_active). - Auf die Felder greifst du mit
.0,.1,.2zu, oder du packst sie per Destrukturieren alle auf einmal aus. - Mit Tupeln gibst du ganz natürlich mehrere Werte aus einer Funktion zurück.
- Die Felder eines
mut-Tupels können sich ändern, seine Größe und Typen aber nicht.(a, b) = (b, a);tauscht zwei Werte. - Für benannte, wiederverwendbare Gruppierungen nimmst du besser ein Struct.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Was kann ein Tupel enthalten, ein Array aber nicht?
2. Wie greifst du auf das zweite Element eines Tupels namens person zu?
3. Welche Werte haben a und b nach let mut a = 1; let mut b = 2; (a, b) = (b, a);?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- Der Tupel-Typ – Das Rust-Buch: die offizielle Einführung dazu, wie du Tupel erstellst, destrukturierst und per Index darauf zugreifst.
- Tuples – Rust by Example (auf Englisch): ausführbare Beispiele mit verschachtelten Tupeln und Tupeln als Funktionsargumente.
- Der Typ
tuple– Doku der Standardbibliothek (auf Englisch): welche Traits Tupel unterstützen, etwa Vergleichen, Kopieren und Ausgeben mit{:?}. - Der Typ
unit– Doku der Standardbibliothek (auf Englisch): was()ist und wo es auftaucht. - Destructuring assignments – The Rust Reference (auf Englisch): die genauen Regeln hinter
(a, b) = (b, a);.