Errore Rust E0284: type annotations needed per parse() (tipo mancante)
E0284 vuol dire quasi sempre una cosa sola: hai chiamato .parse() e Rust non sa in cosa trasformare il testo. La nota cannot satisfy `<_ as FromStr>::Err == _` sembra spaventosa, ma di solito la soluzione è lunga pochi caratteri, come : u32 o ::<u32>. Questa pagina ti dà prima la soluzione, poi decifra quella nota, così smette di fare paura. Per gli altri errori di questa serie, vedi Errori del compilatore Rust.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0284]: type annotations needed --> src/main.rs:2:9 |2 | let age = "42".parse().unwrap(); | ^^^ ----- type must be known at this point | = note: cannot satisfy `<_ as FromStr>::Err == _`help: consider giving `age` an explicit type |2 | let age: /* Type */ = "42".parse().unwrap(); | ++++++++++++Il compilatore ti dice: «servono annotazioni di tipo». Il tipo deve essere noto nel punto di parse, e ti suggerisce di dare a age un tipo esplicito.
Ecco il programma che lo provoca:
fn main() { let age = "42".parse().unwrap(); println!("Next year you'll be {}", age + 1);}Vedrai la stessa nota con .expect(...) al posto di .unwrap(), con match "42".parse() { ... } e con .parse() dentro una closure. Quando .parse() non viene salvato in una variabile, rustc indica parse stesso e suggerisce parse::</* Type */>().
Cosa significa
Sezione intitolata “Cosa significa”I segni sono la parte facile:
^^^sottoage: la variabile di cui non si conosce il tipo.-----sottoparse: «type must be known at this point» (il tipo deve essere noto a questo punto). È qui che Rust si è bloccato.- L’help: «consider giving
agean explicit type» (prova a dare adageun tipo esplicito). Sostituisci/* Type */con un tipo vero, comeu32.
Ora la nota, pezzo per pezzo. Nei messaggi del compilatore, _ vuol dire «un tipo che ancora non conosco»:
| Pezzo | In parole semplici |
|---|---|
<_ as FromStr> |
«il tipo in cui stai convertendo il testo (non lo conosco ancora)» |
::Err |
«…e il tipo di errore che lo accompagna» |
== _ |
«deve essere uguale a un altro tipo che pure non conosco» |
Quindi la nota intera dice: «Non riesco nemmeno a capire che tipo di errore potrebbe restituire parse, perché non so in cosa stai convertendo.»
Pensa a dare una lettera a un traduttore dicendo solo «traducila». In francese? In giapponese? Finché non lo dici, non può cominciare, e non può nemmeno dirti in quali errori potrebbe incappare.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Ecco la firma di parse, un po’ semplificata:
fn parse<F: FromStr>(&self) -> Result<F, F::Err>F è il tipo che vuoi ottenere, e F::Err è il suo tipo di errore. Ogni tipo di destinazione porta il suo: convertire in i32 può fallire con un ParseIntError, in f64 con un ParseFloatError, in bool con un ParseBoolError. A Rust serve F per sapere cosa restituisce parse.
La scelta cambia anche cosa succede a runtime. "42" si converte senza problemi in u8, i32 o f64. Ma "300" fallisce come u8 (troppo grande), e "4.5" fallisce come i32 (non è un numero intero). Rust non tira a indovinare, perché un’ipotesi sbagliata diventerebbe un bug che salta fuori solo con certi input. La lezione Conversioni mostra come funziona FromStr, e come aggiungerlo ai tuoi tipi.
Come Rust deduce i tipi da solo è spiegato nella pagina di E0282. Perché esistono tre codici diversi (E0282, E0283, E0284) per «type annotations needed» è spiegato nella pagina di E0283. In breve: E0284 è la variante in cui la regola bloccata riguarda un tipo che appartiene al tipo sconosciuto (qui F::Err). Per te la soluzione è la stessa: nomina il tipo.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: annota la variabile
Sezione intitolata “Soluzione 1: annota la variabile”Scegli questa strada nella maggior parte dei casi. È lo stile che usa il Rust Book, e documenta cosa deve contenere il testo:
fn main() { let age: u32 = "42".parse().unwrap(); let price: f64 = "4.99".parse().unwrap(); let port: u16 = "8080".parse().expect("la porta deve essere un numero");
println!("L'anno prossimo avrai {} anni", age + 1); println!("Due caffè: {:.2}", price * 2.0); println!("In ascolto sulla porta {port}");}Output:
L'anno prossimo avrai 43 anniDue caffè: 9.98In ascolto sulla porta 8080Scegli il tipo che corrisponde al significato del testo: u16 per una porta, u8 per una percentuale, f64 per un prezzo. Un tipo troppo piccolo trasforma un input valido in un errore, quindi nel dubbio scegli un tipo più capiente come i64 o u32.
Soluzione 2: usa il turbofish parse::<T>()
Sezione intitolata “Soluzione 2: usa il turbofish parse::<T>()”Scegli questa strada quando non c’è una variabile da annotare: dentro una closure, in mezzo a una catena di iteratori, o in un’espressione come ... * 2:
fn main() { let line = "3 7 5"; let total = line .split(' ') .map(|s| s.parse::<u32>().unwrap()) .sum::<u32>(); let doubled = "21".parse::<i32>().unwrap() * 2;
println!("Totale: {total}"); println!("Doppio: {doubled}");}Output:
Totale: 15Doppio: 42Metti il turbofish su parse stesso. Dentro una catena, annotare solo il risultato finale spesso non basta; vedi il primo punto di «Dove lo incontri di solito» qui sotto.
Soluzione 3: nomina il tipo e gestisci bene l’errore
Sezione intitolata “Soluzione 3: nomina il tipo e gestisci bene l’errore”Scegli questa strada per l’input vero (file, argomenti da riga di comando, moduli web), dove .unwrap() farebbe andare in crash il programma con un testo sbagliato. Nomina il tipo su parse e fai match sul Result. Nota come il tipo decide quali input vanno a buon fine:
fn main() { for input in ["42", "300", "abc"] { match input.parse::<u8>() { Ok(n) => println!("{input}: letto {n}"), Err(e) => println!("{input}: {e}"), } }
let rate: Result<f64, _> = "0.25".parse(); println!("{:?}", rate);}Output:
42: letto 42300: number too large to fit in target typeabc: invalid digit found in stringOk(0.25)I messaggi di errore arrivano dalla libreria standard e restano in inglese: «numero troppo grande per il tipo di destinazione» e «cifra non valida nella stringa». In Result<f64, _> nomini solo il tipo del valore. Il _ lascia a Rust il tipo di errore (ParseFloatError), che è proprio la parte di cui parlava la nota. Trovi di più in Result.
Soluzione 4: usa il valore dove il suo tipo è fissato
Sezione intitolata “Soluzione 4: usa il valore dove il suo tipo è fissato”Scegli questa strada quando il valore convertito finisce in un campo di struct o in un parametro di funzione con un tipo noto. Rust legge l’intera funzione, quindi quell’uso successivo risponde alla domanda:
struct Config { port: u16, workers: u8,}
fn greet(age: u32) { println!("Benvenuto! Hai {age} anni.");}
fn main() { let port = "8080".parse().unwrap(); // u16, da Config let workers = "4".parse().unwrap(); // u8, da Config let config = Config { port, workers }; println!("Porta {} con {} worker", config.port, config.workers);
let age = "42".parse().unwrap(); // u32, da greet greet(age);}Output:
Porta 8080 con 4 workerBenvenuto! Hai 42 anni.Non tutti gli usi contano, ed è per questo che è la Soluzione 4. Operazioni come age + 1 e confronti come age >= limit danno ancora E0284, anche quando limit è un u32: gli operatori funzionano tra molte coppie di tipi, quindi non nominano un solo tipo esatto. E chiamare un metodo, come age.pow(2), trasforma l’errore in E0282. Nel dubbio, usa la Soluzione 1.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Convertire dentro map, poi sum(). let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); dà ancora E0284, anche se total ha un tipo. Un u32 può essere la somma di valori u32 oppure di valori &u32, quindi l’annotazione non dice a Rust cos’è ogni elemento. Soluzione: s.parse::<u32>(). (Raccogliere invece in un Vec<i32> funziona, perché un Vec<i32> può contenere solo i32.)
Raccogliere i risultati di parse in un Result. let nums: Result<Vec<_>, _> = text.split(' ').map(|x| x.parse()).collect(); è E0284: il _ in Vec<_> lascia aperto il tipo degli elementi. Soluzione: Result<Vec<i32>, _>. Questo pattern comodo si ferma al primo elemento sbagliato e ti dà il suo errore.
Leggere l’input dell’utente. let guess = line.trim().parse().unwrap(); in un gioco «indovina il numero» o in uno strumento da riga di comando. Soluzione: let guess: u32 = ..., esattamente come nel Rust Book.
Con ? ottieni un errore più strano. let port = text.parse()?; non dà E0284. Nell’edizione 2021, Rust ripiega sul tipo () e segnala E0277: the trait bound `(): FromStr` is not satisfied (l’edizione 2024 dice ! invece di ()). Stessa soluzione: let port: u16 = text.parse()?;.
Errori correlati
Sezione intitolata “Errori correlati”- E0282: servono annotazioni di tipo, quando non c’è nessun indizio (o il tipo serve per chiamare un metodo come
age.pow(2)). - E0283: servono annotazioni di tipo dopo
.sum(),.collect()o.into(), quando vanno bene molti tipi. Spiega anche perché esistono tre codici diversi. - E0790:
FromStr::from_str("42")oDefault::default()chiamati senza dire quale tipo. - E0277: quello che ti dà
.parse()?senza un tipo, e quello che ottieni quando il tipo scelto non si può ricavare da un testo. - E0599: nessun metodo trovato, per esempio chiamando
.parse()su qualcosa che non è testo, come una variabileu32(convertila invece conu8::try_from(n)). - Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Cosa vuol dire, in parole semplici, cannot satisfy <_ as FromStr>::Err == _?
2. Quale riga corregge let port = "8080".parse().unwrap();?
3. let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); dà ancora E0284. Dove va il tipo?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0284 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
str::parse— documentazione della libreria standard (in inglese): la firma del metodo, con sia lo stile con l’annotazione sia quello con il turbofish.FromStr— documentazione della libreria standard (in inglese): il trait dietroparse, con il suo tipoErre come implementarlo.- Programming a Guessing Game — The Rust Book (in inglese): dove compare per la prima volta
let guess: u32 = guess.trim().parse(), spiegato passo per passo. - Data Types — The Rust Book (in inglese): perché
"42".parse()ha bisogno di un’annotazione di tipo, con il vero errore del compilatore.