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.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”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 coveredCosa significa
Sezione intitolata “Cosa significa”Qui fanno tutto due parole:
- Un pattern confutabile (refutable) può non corrispondere.
Some(top)fallisce quando il valore èNone.Ok(age)fallisce suErr.[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, comelet (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.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”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.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: let ... else per uscire presto
Sezione intitolata “Soluzione 1: let ... else per uscire presto”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: 95Porta: 3000'abc' non è una porta valida, uso la 8080Porta: 8080Non 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à: 42Non riesco a leggere 'quarantadue': invalid digit found in stringhost = localhost, porta = 8080'localhost' dovrebbe essere nella forma host:portaCiao, sconosciuto!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 quifn 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: 0Volume: 50Ok(150)Err(ParseIntError { kind: InvalidDigit })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=0localhost:8080 in (3, -1)tema -> scuroLo 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.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”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.
Errori correlati
Sezione intitolata “Errori correlati”- E0004: non-exhaustive patterns: lo stesso controllo «not covered», per
matchinvece che perlet. - E0308: mismatched types: il pattern non va proprio bene per il tipo, per esempio
let Some(x) = 5;. - E0277: trait bound not satisfied: quello che ottieni quando usi
?in una funzione che non restituisceResultoOption, come un semplicefn main(). - E0282: type annotations needed: compare spesso vicino a
parse(), quando Rust non capisce che tipo vuoi. Aggiungi::<i32>come negli esempi qui sopra. - Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”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
Per approfondire
Sezione intitolata “Per approfondire”- E0005 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- Refutability — The Rust Book (in inglese): il capitolo a cui rimanda rustc, sui pattern che possono fallire e su quelli che non possono.
- All the Places Patterns Can Be Used — The Rust Book (in inglese):
let,if let,while let, i ciclifore i parametri delle funzioni. - let-else — Rust by Example (in inglese): brevi esempi eseguibili di
let ... else. - if let — Rust by Example (in inglese): quando
if letsi legge meglio di unmatchcompleto.