Zum Inhalt springen

if let und while let in Rust

Du hast match schon benutzt, um Option und Result zu behandeln. Meistens interessiert dich aber nur ein einziges Muster, und alles andere willst du ignorieren. Ein komplettes match mit einem _ => {}-Arm, der nichts tut, funktioniert zwar. Es ist aber mehr Aufwand, als die Situation verlangt. if let ist die Abkürzung dafür.

fn main() {
let age: Option<i32> = Some(30);
match age {
Some(value) => println!("Das Alter ist {value}"),
_ => {}, // dieser Fall interessiert uns eigentlich nicht
}
}

Das _ => {} tut nichts, außer die Regel von match zu erfüllen, dass jeder Fall behandelt werden muss. Wenn du nur bei Some etwas tun willst, ist das mehr Code, als die Idee verdient.

fn main() {
let age: Option<i32> = Some(30);
if let Some(value) = age {
println!("Das Alter ist {value}");
}
}

Ausgabe:

Das Alter ist 30
▶ Selbst ausprobieren

Lies es so: „Wenn age auf das Muster Some(value) passt, führe diesen Block aus – und value enthält den Wert, der darin steckte.“ Ist age gleich None, wird der Block einfach übersprungen. Kein Fehler, keine Ausgabe.

if let kann ein else haben, genau wie ein normales if:

fn main() {
let age: Option<i32> = None;
if let Some(value) = age {
println!("Das Alter ist {value}");
} else {
println!("Kein Alter angegeben");
}
}
▶ Selbst ausprobieren

Mit Result, das du auf der Seite Result kennengelernt hast, funktioniert es genauso:

fn main() {
let input = "42";
if let Ok(number) = input.parse::<i32>() {
println!("Umgewandelt: {number}");
} else {
println!("Das ließ sich nicht umwandeln");
}
}
▶ Selbst ausprobieren

So wie es bei if ein else if gibt, kann auf if let ein else if let folgen. Rust probiert die Muster der Reihe nach aus und führt den ersten Block aus, der passt. Das ist praktisch, wenn du an mehreren Stellen nach einem Wert suchen kannst – in der Reihenfolge, die du bevorzugst:

fn main() {
let nickname: Option<&str> = None;
let full_name: Option<&str> = Some("Ferris Crab");
if let Some(nick) = nickname {
println!("Hi, {nick}!");
} else if let Some(name) = full_name {
println!("Hallo, {name}.");
} else {
println!("Hallo, Fremder.");
}
}

Ausgabe:

Hallo, Ferris Crab.
▶ Selbst ausprobieren

nickname ist None, also wird der erste Block übersprungen. full_name passt auf Some(name), also läuft dieser Block, und das letzte else wird nie erreicht. Du kannst auch eine ganz normale Bedingung einbauen, etwa else if is_admin { ... } – an jeder Stelle der Kette.

Jede Variable gibt es nur in ihrem eigenen Block: nick kannst du im zweiten Block nicht benutzen, und name nicht mehr nach dem Ende der Kette. Brauchst du den ausgepackten Wert für den ganzen restlichen Code, schau dir let-else auf der Seite Pattern Matching an.

while let überträgt dieselbe Idee auf eine Schleife: Sie läuft so lange, wie ein Wert auf ein Muster passt. Besonders praktisch ist das, wenn du eine Collection Stück für Stück leeren willst:

fn main() {
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{top}");
}
}

Ausgabe:

3
2
1
▶ Selbst ausprobieren

.pop() liefert Some(wert), solange noch etwas übrig ist, und None, sobald der Vektor leer ist. In diesem Moment beendet while let die Schleife automatisch. Du musst die Länge nicht selbst prüfen.

Nimm match, wenn du wirklich mehrere Fälle unterschiedlich behandeln musst. Greif zu if let, wenn dich nur ein einziges Muster interessiert und alles andere einfach ignoriert werden soll. if let ersetzt match nicht. Es ist nur eine kürzere Schreibweise für den häufigen Fall „Mich interessiert nur ein Arm“.

Starte mit let mut numbers = vec![4, 8, 15]; und einer laufenden Summe total von 0. Nimm mit while let und .pop() eine Zahl nach der anderen vom Ende, addiere sie jeweils zu total und gib die Zahl und die neue Summe aus. Tipp: total braucht mut, und total += n addiert etwas dazu.

Lösung anzeigen
fn main() {
let mut numbers = vec![4, 8, 15];
let mut total = 0;
while let Some(n) = numbers.pop() {
total += n;
println!("{n} genommen, Summe ist jetzt {total}");
}
}

Ausgabe:

15 genommen, Summe ist jetzt 15
8 genommen, Summe ist jetzt 23
4 genommen, Summe ist jetzt 27
▶ Selbst ausprobieren

.pop() nimmt vom Ende – deshalb kommt die 15 zuerst heraus.

Speichere let input = "17";. Lässt es sich als u32 umwandeln, gib aus, ob das Alter volljährig (18 oder älter) oder minderjährig ist. Klappt die Umwandlung nicht, gib aus, dass es keine Zahl ist. Probier auch einmal input mit "abc" aus. Tipp: Schreib ein normales if / else in den if let-Block.

Lösung anzeigen
fn main() {
let input = "17";
if let Ok(age) = input.parse::<u32>() {
if age >= 18 {
println!("{age} ist volljährig");
} else {
println!("{age} ist minderjährig");
}
} else {
println!("'{input}' ist keine Zahl");
}
}

Ausgabe:

17 ist minderjährig
▶ Selbst ausprobieren

Mit input = "abc" schlägt die Umwandlung fehl, das äußere else läuft, und es wird 'abc' ist keine Zahl ausgegeben.

  • if let Muster = wert { ... } führt den Block nur aus, wenn wert auf Muster passt – alles andere wird still übersprungen.
  • Mit else behandelst du den Fall, in dem das Muster nicht passt, genau wie bei einem normalen if. Mit else if let verkettest du mehrere Muster.
  • while let Muster = wert { ... } wiederholt die Schleife, solange der Wert passt. Das ist praktisch, um eine Collection mit .pop() zu leeren.
  • Nimm if let/while let, wenn dich nur ein Muster interessiert, und match, wenn du mehrere Fälle getrennt behandeln musst.

Kurzer Check

1. Was passiert, wenn if let Some(value) = age ausgeführt wird und age gleich None ist?

2. Wann solltest du match statt if let verwenden?

3. Wann endet die Schleife while let Some(top) = stack.pop() { ... }?

Punkte: 0 / 3