Salta ai contenuti

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.

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 */>().

I segni sono la parte facile:

  1. ^^^ sotto age: la variabile di cui non si conosce il tipo.
  2. ----- sotto parse: «type must be known at this point» (il tipo deve essere noto a questo punto). È qui che Rust si è bloccato.
  3. L’help: «consider giving age an explicit type» (prova a dare ad age un tipo esplicito). Sostituisci /* Type */ con un tipo vero, come u32.

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.

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.

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 anni
Due caffè: 9.98
In ascolto sulla porta 8080
▶ Provalo tu

Scegli 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.

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: 15
Doppio: 42
▶ Provalo tu

Metti 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 42
300: number too large to fit in target type
abc: invalid digit found in string
Ok(0.25)
▶ Provalo tu

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 worker
Benvenuto! Hai 42 anni.
▶ Provalo tu

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.

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()?;.

  • 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") o Default::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 variabile u32 (convertila invece con u8::try_from(n)).
  • Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.

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