Zum Inhalt springen

Rust-Fehler E0005: refutable pattern in local binding (let kann fehlschlagen)

E0005 bedeutet: Du hast ein let geschrieben, dessen Muster vielleicht nicht passt, etwa let Some(x) = value;, obwohl value auch None sein könnte. Rust muss wissen, was in diesem Fall passieren soll, und ein einfaches let hat keinen Platz, um das zu sagen. Die Lösung: Gib dem Fall „passt nicht“ einen Ort, an den er gehen kann – meistens mit let ... else, if let oder match. Dieser Fehler ist sehr häufig, wenn du anfängst, mit Option und Result zu arbeiten, und in einer Minute behoben. Die anderen Fehler dieser Reihe findest du unter Rust-Compilerfehler.

error[E0005]: refutable pattern in local binding
--> src/main.rs:4:9
|
4 | let Some(top) = best; // ❌ und wenn die Liste leer war?
| ^^^^^^^^^ pattern `None` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let...else` to handle the variant that isn't matched
|
4 | let Some(top) = best else { todo!() }; // ❌ und wenn die Liste leer war?
| ++++++++++++++++

„Refutable pattern in local binding“ heißt so viel wie „abweisbares Muster in einer lokalen Bindung“. Das Label sagt: Das Muster None ist nicht abgedeckt. Die erste note erklärt, dass let ein Muster braucht, das nicht fehlschlagen kann, und das help schlägt let...else vor. Dieses Programm erzeugt den Fehler:

fn main() {
let scores = vec![72, 95, 88];
let best = scores.iter().max().copied();
let Some(top) = best; // ❌ und wenn die Liste leer war?
println!("Top score: {top}");
}

Mit anderen Mustern siehst du denselben Fehler. Nur der Teil „not covered“ (nicht abgedeckt) ändert sich:

3 | let Ok(age) = input.parse::<i32>();
| ^^^^^^^ pattern `Err(_)` not covered
3 | let [host, port] = parts.as_slice();
| ^^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _, ..]` not covered

Zwei Wörter erledigen hier die ganze Arbeit:

  • Ein refutable (abweisbares) Muster kann fehlschlagen. Some(top) schlägt fehl, wenn der Wert None ist. Ok(age) schlägt bei Err fehl. [host, port] schlägt fehl, wenn der Slice nicht genau zwei Elemente hat.
  • Ein irrefutable (unabweisbares) Muster passt immer. Ein einfacher Name wie in let top = best; funktioniert immer, und Tupel und Structs wie in let (x, y) = point; auch.

Ein let muss immer klappen, denn die nächste Zeile benutzt die Variablen, die es anlegt. Wäre best gleich None, was wäre dann top in der nächsten Zeile? Darauf gibt es keine Antwort, also hält Rust dich auf. Das Label „not covered“ listet genau die Fälle auf, die du vergessen hast: None, Err(_) oder beim Slice „leer“, „ein Element“ und „drei oder mehr Elemente“.

Stell dir eine Paketstation vor. let Some(top) = best; ist so, als würdest du weggehen, fest überzeugt, dass ein Paket im Fach liegt – ohne die Tür aufzumachen. Rust zwingt dich zu entscheiden, was du tust, wenn das Fach leer ist.

let, Funktionsparameter und for-Schleifen brauchen Muster, die nicht fehlschlagen können. match, if let, while let und let ... else nehmen Muster an, die fehlschlagen können, weil jedes davon einen Platz für den anderen Fall hat. Das ist dieselbe Regel wie bei E0004, wo ein match jeden Wert abdecken muss. Hier ist das „match“ ein let mit nur einem Zweig.

In vielen Sprachen bekommst du beim Lesen eines Werts, der nicht da ist, null, undefined oder einen Absturz zur Laufzeit. In Rust sind eine leere Liste, eine falsche Zahl in einem Textfeld oder eine fehlende Einstellung normale Fälle, die du im Code behandelst. Option und Result machen sie sichtbar, und E0005 sorgt dafür, dass du sie nicht aus Versehen überspringst. Siehe Muster in let und Option.

Nimm diese Lösung, wenn der Rest der Funktion den Wert braucht und es ohne ihn nichts Sinnvolles zu tun gibt. Genau das schlägt rustc im help: vor. Du behältst dein Muster und fügst einen else-Block hinzu. Dieser Block muss den Code verlassen: mit return, break, continue oder einer Panic.

fn port_from(text: &str) -> u16 {
// die Zahl herausholen oder die Funktion früh verlassen
let Ok(port) = text.trim().parse::<u16>() else {
println!("'{text}' ist kein gültiger Port, nehme 8080");
return 8080;
};
port
}
fn main() {
let scores = vec![72, 95, 88];
let Some(top) = scores.iter().max() else {
println!("Noch keine Punktzahlen");
return;
};
println!("Höchste Punktzahl: {top}");
println!("Port: {}", port_from("3000"));
println!("Port: {}", port_from("abc"));
}

Ausgabe:

Höchste Punktzahl: 95
Port: 3000
'abc' ist kein gültiger Port, nehme 8080
Port: 8080
▶ Selbst ausprobieren

Lass das else { todo!() } von rustc nicht einfach so stehen. todo!() löst beim Ausführen eine Panic aus, also ersetz es durch einen echten Ausweg. Mehr dazu unter let-else.

Lösung 2: match oder if let, wenn beide Fälle etwas tun

Abschnitt betitelt „Lösung 2: match oder if let, wenn beide Fälle etwas tun“

Nimm diese Lösung, wenn der Fall „passt nicht“ eigene Arbeit hat, etwa den Fehler ausgeben, oder wenn der Code in beiden Fällen weiterläuft. Mit match behandelst du jeden Fall und kannst den Fehlerwert benutzen. So nimmst du auch einen Slice auseinander, dessen Länge du nicht kennst. Nimm if let, wenn nur ein Fall zählt:

fn main() {
// match: beide Ergebnisse behandeln und den Fehler benutzen
for input in ["42", "zweiundvierzig"] {
match input.parse::<i32>() {
Ok(age) => println!("Alter: {age}"),
Err(e) => println!("Kann '{input}' nicht lesen: {e}"),
}
}
// ein Slice kann jede Länge haben: match auf seine Form
for address in ["localhost:8080", "localhost"] {
let parts: Vec<&str> = address.split(':').collect();
match parts.as_slice() {
[host, port] => println!("host = {host}, port = {port}"),
_ => println!("'{address}' sollte wie host:port aussehen"),
}
}
// if let: nur etwas tun, wenn ein Wert da ist
let nickname: Option<&str> = None;
if let Some(name) = nickname {
println!("Hallo, {name}!");
} else {
println!("Hallo, unbekannter Gast!");
}
}

Ausgabe:

Alter: 42
Kann 'zweiundvierzig' nicht lesen: invalid digit found in string
host = localhost, port = 8080
'localhost' sollte wie host:port aussehen
Hallo, unbekannter Gast!
▶ Selbst ausprobieren

„invalid digit found in string“ ist die Fehlermeldung von Rust selbst und heißt „ungültige Ziffer im String gefunden“. Der Zweig _ fängt jede andere Länge ab. Ohne ihn würde match dir E0004 mit derselben „not covered“-Liste geben. Siehe if let und match.

Lösung 3: Ein Standardwert mit unwrap_or oder weitergeben mit ?

Abschnitt betitelt „Lösung 3: Ein Standardwert mit unwrap_or oder weitergeben mit ?“

Nimm diese Lösung, wenn es einen sinnvollen Standardwert gibt oder wenn sich der Aufrufer um das Problem kümmern soll. unwrap_or(x) gibt dir den Wert, oder x, wenn keiner da ist. ? funktioniert in einer Funktion, die Result (oder Option) zurückgibt: Es gibt den Fehler an den Aufrufer zurück und beendet die Funktion.

use std::num::ParseIntError;
// ? reicht den Fehler an den Aufrufer weiter, statt ihn hier zu behandeln
fn total_minutes(hours: &str, minutes: &str) -> Result<i32, ParseIntError> {
let h = hours.parse::<i32>()?;
let m = minutes.parse::<i32>()?;
Ok(h * 60 + m)
}
fn main() {
// ein Standardwert, wenn nichts da ist
let scores: Vec<i32> = Vec::new();
let top = scores.iter().max().copied().unwrap_or(0);
println!("Höchste Punktzahl: {top}");
let volume = "laut".parse::<u8>().unwrap_or(50);
println!("Lautstärke: {volume}");
println!("{:?}", total_minutes("2", "30"));
println!("{:?}", total_minutes("2", "halb"));
}

Ausgabe:

Höchste Punktzahl: 0
Lautstärke: 50
Ok(150)
Err(ParseIntError { kind: InvalidDigit })
▶ Selbst ausprobieren

Bist du dir sicher, dass der Wert da ist (zum Beispiel bei einer Zahl, die du gerade selbst in den Code geschrieben hast), ist auch .expect("Grund") in Ordnung. Es löst eine Panic mit deiner Meldung aus, falls du dich geirrt hast. Heb es also für Fälle auf, die wirklich nicht schiefgehen können. ? in einem main, das nichts zurückgibt, ergibt E0277. Siehe Der ?-Operator und Abkürzungen: unwrap_or.

Lösung 4: Einen Typ nehmen, dessen Muster nicht fehlschlagen kann

Abschnitt betitelt „Lösung 4: Einen Typ nehmen, dessen Muster nicht fehlschlagen kann“

Nimm diese Lösung, wenn die Daten immer dieselbe Form haben und das Muster nur wegen des gewählten Typs fehlschlagen kann. Ein Slice oder Vec kann jede Länge haben, aber ein Array wie [u8; 3] hat immer genau drei Elemente. Deshalb passt let [r, g, b] = color; immer. Tupel und Structs passen ebenfalls immer. Und split_once gibt dir beide Hälften als ein Tupel, sodass du mit einer einzigen Option zu tun hast statt mit einem Slice unbekannter Länge:

struct Point {
x: i32,
y: i32,
}
fn main() {
// Arrays haben eine feste Länge: dieses Muster passt immer
let color: [u8; 3] = [255, 140, 0];
let [r, g, b] = color;
println!("r={r} g={g} b={b}");
// Tupel und Structs passen auch immer
let (host, port) = ("localhost", 8080);
let Point { x, y } = Point { x: 3, y: -1 };
println!("{host}:{port} bei ({x}, {y})");
// split_once gibt eine Option mit beiden Hälften zurück
let Some((key, value)) = "theme=dark".split_once('=') else {
return;
};
println!("{key} -> {value}");
}

Ausgabe:

r=255 g=140 b=0
localhost:8080 bei (3, -1)
theme -> dark
▶ Selbst ausprobieren

Das gilt auch für deine eigenen Enums: let Shape::Circle(radius) = shape; schlägt mit pattern `Shape::Square(_)` not covered fehl. Kann der Wert wirklich nur ein Kreis sein, sollte er vielleicht ein Struct Circle sein statt eines Enums. Siehe Arrays und Structs.

Eine for-Schleife über Options. for Some(temp) in readings ergibt „refutable pattern in for loop binding“ (abweisbares Muster in der Bindung einer for-Schleife). Lösung: for temp in readings.iter().flatten() überspringt die Nones. Oder du läufst über jedes Element und benutzt darin if let Some(t) = reading.

Ein Funktionsparameter. fn greet(Some(name): Option<&str>) ergibt „refutable pattern in function argument“ (abweisbares Muster in einem Funktionsargument). Lösung: Nimm name: Option<&str> und mach das match innerhalb der Funktion. Oder nimm name: &str und lass den Aufrufer entscheiden, was bei None passiert.

Eingaben parsen. let Ok(n) = line.trim().parse::<i32>(); beim Lesen von Benutzereingaben oder Kommandozeilenargumenten. Lösung: let Ok(n) = ... else { println!("Bitte gib eine Zahl ein"); continue; }; innerhalb der Eingabeschleife, oder ein match, um den Fehler auszugeben.

Kurzer Check

1. Welches let ergibt E0005?

2. Was muss der else-Block eines let ... else tun?

3. Du brauchst eine Zahl aus einem Text, und 0 ist ein guter Standardwert, wenn der Text keine Zahl ist. Was ist die kürzeste Lösung?

Punkte: 0 / 3