Salta ai contenuti

Errore Rust E0005: refutable pattern in local binding (pattern confutabile)

E0005 significa che hai scritto un let il cui pattern potrebbe non corrispondere, come let Some(x) = value; quando value può essere None. Rust deve sapere cosa succede in quel caso, e un semplice let non ha un posto dove dirlo. La soluzione è dare una strada al caso «non corrisponde»: di solito let ... else, if let o match. È un errore molto comune quando inizi a lavorare con Option e Result, e si risolve in un minuto. Per gli altri errori di questa serie, vedi Errori del compilatore Rust.

error[E0005]: refutable pattern in local binding
--> src/main.rs:4:9
|
4 | let Some(top) = best; // ❌ what if the list was empty?
| ^^^^^^^^^ 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!() }; // ❌ what if the list was empty?
| ++++++++++++++++

Il compilatore ti dice: «pattern confutabile in un binding locale», e il caso None non è coperto. Ricorda che un let richiede un pattern che corrisponde sempre, e ti suggerisce let...else per gestire la variante mancante.

Ecco il programma che lo provoca:

fn main() {
let scores = vec![72, 95, 88];
let best = scores.iter().max().copied();
let Some(top) = best; // ❌ what if the list was empty?
println!("Top score: {top}");
}

(Il commento dice: e se la lista fosse vuota?)

Vedrai lo stesso errore con altri pattern. Cambia solo la parte «not covered» (non coperto):

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

Qui fanno tutto due parole:

  • Un pattern confutabile (refutable) può non corrispondere. Some(top) fallisce quando il valore è None. Ok(age) fallisce su Err. [host, port] fallisce quando la slice non ha esattamente due elementi.
  • Un pattern inconfutabile (irrefutable) corrisponde sempre. Un semplice nome come let top = best; funziona sempre, e così le tuple e le struct, come let (x, y) = point;.

Un let deve sempre riuscire, perché la riga successiva usa le variabili che crea. Se best fosse None, cosa sarebbe top alla riga dopo? Non c’è risposta, quindi Rust ti ferma. L’etichetta «not covered» elenca esattamente i casi che hai dimenticato: None, Err(_), oppure, per la slice, «vuota», «un elemento» e «tre o più elementi».

Pensa a un armadietto per i pacchi. let Some(top) = best; è come andartene sicuro che dentro ci sia un pacco, senza aprire lo sportello. Rust ti obbliga a decidere cosa fare se l’armadietto è vuoto.

let, i parametri delle funzioni e i cicli for vogliono tutti pattern che non possono fallire. match, if let, while let e let ... else accettano pattern che possono fallire, perché ognuno di loro ha un posto per l’altro caso. È la stessa regola di E0004, dove un match deve coprire tutti i valori. Qui il «match» è un let con un solo ramo.

In molti linguaggi, leggere un valore che non c’è ti dà null, undefined o un crash a runtime. In Rust una lista vuota, un numero sbagliato in un campo di testo o un’impostazione mancante sono casi normali che gestisci nel codice. Option e Result li rendono visibili, ed E0005 si assicura che tu non li salti per sbaglio. Vedi Pattern matching e Option.

Scegli questa strada quando il resto della funzione ha bisogno del valore, e senza di esso non c’è niente di utile da fare. È quello che suggerisce l’help: di rustc. Tieni il tuo pattern e aggiungi un blocco else. Quel blocco deve uscire: return, break, continue o un panic.

fn port_from(text: &str) -> u16 {
// tira fuori il numero, oppure esci subito dalla funzione
let Ok(port) = text.trim().parse::<u16>() else {
println!("'{text}' non è una porta valida, uso la 8080");
return 8080;
};
port
}
fn main() {
let scores = vec![72, 95, 88];
let Some(top) = scores.iter().max() else {
println!("Ancora nessun punteggio");
return;
};
println!("Punteggio migliore: {top}");
println!("Porta: {}", port_from("3000"));
println!("Porta: {}", port_from("abc"));
}

Output:

Punteggio migliore: 95
Porta: 3000
'abc' non è una porta valida, uso la 8080
Porta: 8080
▶ Provalo tu

Non lasciare così com’è l’else { todo!() } di rustc. todo!() va in panic quando viene eseguito, quindi sostituiscilo con una vera via d’uscita. Trovi di più su let ... else in Pattern matching.

Soluzione 2: match o if let quando entrambi i casi fanno qualcosa

Sezione intitolata “Soluzione 2: match o if let quando entrambi i casi fanno qualcosa”

Scegli questa strada quando anche il caso «non corrisponde» ha un suo lavoro da fare, come stampare l’errore, oppure quando il codice continua in ogni caso. match ti permette di gestire tutti i casi e di usare il valore dell’errore. È anche il modo per scomporre una slice di cui non conosci la lunghezza. Usa if let quando conta un solo caso:

fn main() {
// match: gestisce entrambi i risultati e usa l'errore
for input in ["42", "quarantadue"] {
match input.parse::<i32>() {
Ok(age) => println!("Età: {age}"),
Err(e) => println!("Non riesco a leggere '{input}': {e}"),
}
}
// una slice può avere qualsiasi lunghezza: fai match sulla sua forma
for address in ["localhost:8080", "localhost"] {
let parts: Vec<&str> = address.split(':').collect();
match parts.as_slice() {
[host, port] => println!("host = {host}, porta = {port}"),
_ => println!("'{address}' dovrebbe essere nella forma host:porta"),
}
}
// if let: fai qualcosa solo quando c'è un valore
let nickname: Option<&str> = None;
if let Some(name) = nickname {
println!("Ciao, {name}!");
} else {
println!("Ciao, sconosciuto!");
}
}

Output:

Età: 42
Non riesco a leggere 'quarantadue': invalid digit found in string
host = localhost, porta = 8080
'localhost' dovrebbe essere nella forma host:porta
Ciao, sconosciuto!
▶ Provalo tu

Il messaggio invalid digit found in string (cifra non valida nella stringa) arriva dalla libreria standard, quindi resta in inglese. Il ramo _ cattura tutte le altre lunghezze. Senza, match ti darebbe E0004 con lo stesso elenco «not covered». Vedi if let e match.

Soluzione 3: un valore predefinito con unwrap_or, o passa l’errore con ?

Sezione intitolata “Soluzione 3: un valore predefinito con unwrap_or, o passa l’errore con ?”

Scegli questa strada quando esiste un valore predefinito sensato, oppure quando deve occuparsi del problema chi chiama la funzione. unwrap_or(x) ti dà il valore, oppure x se non c’è. ? funziona dentro una funzione che restituisce Result (o Option): restituisce l’errore al chiamante e ferma la funzione.

use std::num::ParseIntError;
// ? passa l'errore al chiamante invece di gestirlo qui
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() {
// un valore predefinito quando non c'è niente
let scores: Vec<i32> = Vec::new();
let top = scores.iter().max().copied().unwrap_or(0);
println!("Punteggio migliore: {top}");
let volume = "forte".parse::<u8>().unwrap_or(50);
println!("Volume: {volume}");
println!("{:?}", total_minutes("2", "30"));
println!("{:?}", total_minutes("2", "mezza"));
}

Output:

Punteggio migliore: 0
Volume: 50
Ok(150)
Err(ParseIntError { kind: InvalidDigit })
▶ Provalo tu

Se sei sicuro che il valore c’è (per esempio un numero che hai appena scritto nel codice), va bene anche .expect("motivo"). Va in panic con il tuo messaggio se ti sbagliavi, quindi tienilo per i casi che davvero non possono fallire. Usare ? in un main che non restituisce niente dà E0277; vedi Result e Option.

Soluzione 4: usa un tipo il cui pattern non può fallire

Sezione intitolata “Soluzione 4: usa un tipo il cui pattern non può fallire”

Scegli questa strada quando i dati hanno sempre la stessa forma, e il pattern fallisce solo per colpa del tipo che hai scelto. Una slice o un Vec possono avere qualsiasi lunghezza, ma un array come [u8; 3] ha sempre esattamente tre elementi, quindi let [r, g, b] = color; corrisponde sempre. Anche tuple e struct corrispondono sempre. E split_once ti dà le due metà in una sola tupla, così gestisci un solo Option invece di una slice di lunghezza sconosciuta:

struct Point {
x: i32,
y: i32,
}
fn main() {
// gli array hanno una lunghezza fissa: questo pattern corrisponde sempre
let color: [u8; 3] = [255, 140, 0];
let [r, g, b] = color;
println!("r={r} g={g} b={b}");
// anche tuple e struct corrispondono sempre
let (host, port) = ("localhost", 8080);
let Point { x, y } = Point { x: 3, y: -1 };
println!("{host}:{port} in ({x}, {y})");
// split_once restituisce un solo Option con entrambe le metà
let Some((key, value)) = "tema=scuro".split_once('=') else {
return;
};
println!("{key} -> {value}");
}

Output:

r=255 g=140 b=0
localhost:8080 in (3, -1)
tema -> scuro
▶ Provalo tu

Lo stesso vale per i tuoi enum: let Shape::Circle(radius) = shape; fallisce con pattern `Shape::Square(_)` not covered. Se il valore può davvero essere solo un cerchio, forse dovrebbe essere una struct Circle invece di un enum. Vedi Array e Struct.

Un ciclo for su degli Option. for Some(temp) in readings dà «refutable pattern in for loop binding». Soluzione: for temp in readings.iter().flatten() salta i None, oppure scorri tutti gli elementi e usa if let Some(t) = reading all’interno.

Un parametro di funzione. fn greet(Some(name): Option<&str>) dà «refutable pattern in function argument». Soluzione: ricevi name: Option<&str> e fai match dentro la funzione, oppure ricevi name: &str e lascia decidere al chiamante cosa fare con None.

Leggere l’input. let Ok(n) = line.trim().parse::<i32>(); quando leggi l’input dell’utente o gli argomenti da riga di comando. Soluzione: let Ok(n) = ... else { println!("Scrivi un numero"); continue; }; dentro il ciclo di input, oppure un match per stampare l’errore.

Verifica veloce

1. Quale let dà E0005?

2. Cosa deve fare il blocco else di un let ... else?

3. Ti serve un numero da un testo, e 0 va bene come valore predefinito se il testo non è un numero. Qual è la soluzione più breve?

Punteggio: 0 / 3