if let und while let sind syntaktische Abkürzungen für match mit genau einem interessanten Arm. Wenn dich nur ein Pattern interessiert — typischerweise Some(x) aus einem Option oder Ok(x) aus einem Result — ist ein vollständiges match mit einem _ => {}-Arm overkill. if let pattern = value { ... } macht das in einer Zeile. while let läuft eine Schleife, solange ein Pattern matcht. Dieser Artikel zeigt beide Konstrukte, die let chains (seit Edition 2024) und die typischen Anwendungs-Patterns.
if let — kompakter Pattern-Match
fn main() {
let maybe: Option<i32> = Some(42);
// Klassisch mit match:
match maybe {
Some(n) => println!("Wert: {n}"),
None => {} // None ist langweilig
}
// Kompakt mit if let:
if let Some(n) = maybe {
println!("Wert: {n}");
}
}if let pattern = value { ... } läuft den Body nur, wenn das Pattern matcht. Die im Pattern gebundenen Variablen (n) sind im Body verfügbar.
Mit else
fn main() {
let maybe: Option<i32> = Some(42);
if let Some(n) = maybe {
println!("Wert: {n}");
} else {
println!("Kein Wert");
}
}else läuft, wenn das Pattern nicht matcht. Wie ein zwei-armiger match, aber kürzer.
Mit else if let — Kette
enum Event { Click(u32, u32), KeyDown(char), Idle }
fn behandle(e: &Event) -> String {
if let Event::Click(x, y) = e {
format!("Klick bei ({x}, {y})")
} else if let Event::KeyDown(c) = e {
format!("Taste: {c}")
} else {
"Idle".to_string()
}
}Mehrere Patterns in Kette. Bei mehr als 2-3 Varianten wird match aber meist lesbarer.
Wann if let, wann match?
| Situation | Wahl |
|---|---|
| Nur ein Pattern interessiert | if let |
| 2+ Patterns interessieren | match |
| Exhaustiveness-Garantie wichtig | match |
| Wert auch im else-Branch nötig | if let else oder match |
| Drain/Iteration | while let |
Faustregel: if let bei 1 Pattern, match ab 2 — der Compiler macht den Exhaustiveness-Check, was Bugs verhindert.
while let — Pattern-getriebene Schleife
fn main() {
let mut stack = vec![1, 2, 3, 4, 5];
while let Some(top) = stack.pop() {
println!("{top}");
}
// 5, 4, 3, 2, 1
assert!(stack.is_empty());
}pop() gibt Option<T> zurück — Some solange etwas da ist, None bei leerer Vec. while let läuft, solange das Pattern matcht.
Klassisches Drain-Pattern für Container.
let chains (seit Edition 2024)
Mehrere let-Patterns plus boolesche Bedingungen mit &&:
struct Konfig { db: Option<String>, port: Option<u16> }
fn main() {
let c = Konfig { db: Some("postgres://...".into()), port: Some(5432) };
if let Some(url) = &c.db
&& let Some(p) = c.port
&& p > 1024
{
println!("DB={url} auf Port {p}");
}
}Vor Edition 2024 wären das verschachtelte if lets:
# struct Konfig { db: Option<String>, port: Option<u16> }
# let c = Konfig { db: Some("postgres://...".into()), port: Some(5432) };
if let Some(url) = &c.db {
if let Some(p) = c.port {
if p > 1024 {
println!("DB={url} auf Port {p}");
}
}
}let chains machen die natürliche „und"-Logik in einer Zeile. Voraussetzung: Edition 2024 im Cargo.toml.
Mehrere Patterns mit |
if let und while let unterstützen Or-Patterns:
enum Status { Ok(i32), Warning(i32), Error(String) }
fn extrahiere_zahl(s: &Status) -> Option<i32> {
if let Status::Ok(n) | Status::Warning(n) = s {
Some(*n)
} else {
None
}
}Status::Ok(n) | Status::Warning(n) matcht beide Varianten — n ist in beiden gebunden, beide haben den gleichen Typ.
while let mit komplexen Patterns
use std::collections::VecDeque;
fn main() {
let mut queue: VecDeque<(u64, String)> = VecDeque::new();
queue.push_back((1, "first".into()));
queue.push_back((2, "second".into()));
while let Some((id, payload)) = queue.pop_front() {
println!("Job {id}: {payload}");
}
}Pattern-Destrukturierung im while let — sehr typisch für Worker-Loops mit strukturierten Items.
Praxis: if let / while let im echten Code
Option-Verarbeitung
use std::collections::HashMap;
pub fn drucke_cache_wert(cache: &HashMap<String, String>, key: &str) {
if let Some(wert) = cache.get(key) {
println!("Cache-Hit: {wert}");
} else {
println!("Cache-Miss für {key}");
}
}Standard-Pattern für Map-Lookups.
Result-Verarbeitung
pub fn versuche_aufruf() {
if let Ok(inhalt) = std::fs::read_to_string("/etc/hostname") {
println!("Hostname: {}", inhalt.trim());
}
// Bei Err wird einfach nichts gemacht
}Wenn der Fehler unwichtig ist und du nur den Success-Fall behandeln willst.
Drain einer Queue
pub fn verarbeite_alle(queue: &mut Vec<String>) {
while let Some(item) = queue.pop() {
println!("Verarbeite: {item}");
}
}Klassisches Worker-Pattern.
Channel-Empfang
use std::sync::mpsc::Receiver;
pub fn verarbeite_messages(rx: Receiver<String>) {
while let Ok(msg) = rx.recv() {
println!("Erhalten: {msg}");
}
// Schleife endet, wenn der Channel geschlossen wird (Err).
}recv() gibt Result — Ok bei Nachricht, Err bei geschlossenem Channel. Standard-Empfänger-Loop.
Konfigurations-Lookup
pub fn aktiviere_feature_wenn_da(config: &HashMap<String, bool>, feature: &str) {
if let Some(&true) = config.get(feature) {
println!("Feature {feature} aktiv");
}
}
# use std::collections::HashMap;Some(&true) — Pattern auf inneren Wert. Sehr kompakt.
Iterator durchlaufen mit Pattern
pub fn drucke_zahlen(input: &[&str]) {
for s in input {
if let Ok(n) = s.parse::<i32>() {
println!("{n}");
}
// Sonstige: ignorieren
}
}if let innerhalb eines Loops — filtert und transformiert in einem Schritt.
Nested Option mit Edition-2024-Chains
struct Profil { name: String, telefon: Option<String> }
struct Account { profil: Option<Profil> }
pub fn drucke_telefon(a: &Account) {
if let Some(p) = &a.profil
&& let Some(t) = &p.telefon
{
println!("Telefon: {t}");
}
}Edition 2024 macht das aus zwei verschachtelten if lets ein einziger lesbarer Block.
State-Machine-Step mit if let
enum State { Inaktiv, Aktiv(u32), Beendet }
pub fn next(state: State) -> State {
if let State::Aktiv(counter) = state {
if counter > 10 {
State::Beendet
} else {
State::Aktiv(counter + 1)
}
} else {
state
}
}if let mit Variable-Match plus Guard im Body.
Worker-Loop mit komplexer Bedingung
use std::collections::VecDeque;
pub fn verarbeite_jobs(jobs: &mut VecDeque<(u64, i32)>) -> i32 {
let mut summe = 0;
while let Some((id, wert)) = jobs.pop_front() {
if wert < 0 {
println!("Job {id} übersprungen (negative)");
continue;
}
summe += wert;
}
summe
}Drain mit Destrukturierung und Skip-Logik.
Optional-Field-Update
struct Settings { email: Option<String>, telefon: Option<String> }
pub fn email_normalisieren(s: &mut Settings) {
if let Some(email) = &mut s.email {
*email = email.trim().to_lowercase();
}
}if let Some(...) = &mut ... — mutable Pattern, bindet als mutable Referenz. Update direkt am Wert.
FAQ
Wann if let, wann match?
if let bei genau einem interessanten Pattern. match ab zwei. match hat Exhaustiveness-Check, der bei neuen Varianten den Compiler aufmerksam macht — if let „versteckt" andere Fälle. Bei Unsicherheit: match ist sicherer.
while let ist der idiomatische Drain-Loop.
while let Some(x) = container.pop() { ... } läuft, bis der Container leer ist. Klassisch für Worker-Queues, Stack-Verarbeitung, Iterator-Konsumieren.
let chains sind Edition 2024 stable.
if let A && let B && cond { ... } ist seit Rust 1.85 / Edition 2024 stable. Auf älteren Editionen: verschachtelte if lets.
if let mit Or-Patterns funktioniert.
if let Some(x) | NextSome(x) = val { ... } ist legal. Mehrere Patterns mit gleichem Binding-Typ. Sehr expressiv.
Mutable Pattern: if let Some(x) = &mut ....
Für In-Place-Mutation: if let Some(x) = &mut option { *x = neu; }. x ist eine mutable Referenz, durch *x = ... änderst du den inneren Wert.
Mit else wird if let wie 2-armiges match.
if let Some(n) = x { ... } else { ... } ist syntaktisch identisch zu match x { Some(n) => ..., _ => ... }. Geschmackssache.
while let mit iter_mut bricht Borrow-Checker.
Während while let Some(x) = iter.next() läuft, ist die Sammlung über iter geborgt — du kannst sie nicht parallel modifizieren. Bei pop() ist das anders, weil jeder Pop verbraucht — keine bestehende Borrow.
else if let-Ketten sind möglich, aber match ist meist klarer.
if let A = x { ... } else if let B = x { ... } else { ... } funktioniert. Bei 3+ Patterns wird match lesbarer. Faustregel: bei 2 Patterns mit unterschiedlicher Logik — if let / else if let. Bei 3+ — match.
Weiterführende Ressourcen
Externe Quellen
- The Rust Book – if let
- Rust Reference – if let expressions
- Rust Reference – while let
- Rust 2024 Edition – let chains