Errore Rust E0308: mismatched types (tipi non corrispondenti)
L’errore E0308 significa che Rust si aspettava un valore di un certo tipo, ma tu gli hai dato un valore di un tipo diverso. È probabilmente l’errore che vedrai più spesso in Rust, e il messaggio ti dice quasi sempre entrambi i tipi, quindi di solito si risolve in fretta. (Cerchi un altro errore? Vedi la panoramica degli errori del compilatore.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0308]: mismatched types --> src/main.rs:6:29 |6 | let user = User { name: "Ferris" }; | ^^^^^^^^ expected `String`, found `&str` |help: try using a conversion method |6 | let user = User { name: "Ferris".to_string() }; | ++++++++++++Il compilatore ti dice: «tipi non corrispondenti: atteso String, trovato &str», e ti suggerisce un metodo di conversione.
Ecco il programma che lo produce:
struct User { name: String,}
fn main() { let user = User { name: "Ferris" }; println!("{}", user.name);}Cosa significa
Sezione intitolata “Cosa significa”Ogni messaggio E0308 ha la stessa forma: expected A, found B (atteso A, trovato B).
- Expected (atteso) è il tipo di cui Rust ha bisogno in quel punto. Viene da qualche altra parte: un’annotazione di tipo, il parametro o il tipo di ritorno di una funzione, il campo di una struct, o un altro ramo di un
if/match. - Found (trovato) è il tipo del valore che hai scritto davvero lì.
Nell’esempio, il campo name è dichiarato come String, quindi è quello che Rust si aspetta. Ma "Ferris" è un letterale stringa, che ha tipo &str. Sono parenti, ma non sono lo stesso tipo.
Guarda anche le righe help:. Per E0308 il compilatore spesso suggerisce la soluzione esatta, come .to_string() qui sopra.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Rust non converte mai da un tipo all’altro di nascosto. In alcuni linguaggi un numero diventa silenziosamente una stringa, o un numero grande viene tagliato in silenzio per farlo stare in uno più piccolo. Quelle conversioni nascoste causano bug reali: dati persi, risultati sbagliati, crash lontanissimi dal vero errore.
Quindi Rust rende ogni conversione visibile nel codice. Si scrive un po’ di più, ma quando rileggi il codice vedi esattamente dove un valore cambia tipo e se quel passaggio può fallire. Vedi Tipi di dati per i tipi veri e propri.
Come risolverlo
Sezione intitolata “Come risolverlo”Ogni soluzione qui sotto parte dalla forma dell’errore, così puoi saltare a quella che corrisponde al tuo.
Soluzione 1: togli il punto e virgola finale
Sezione intitolata “Soluzione 1: togli il punto e virgola finale”error[E0308]: mismatched types --> src/main.rs:1:46 |1 | fn total_price(price: u32, quantity: u32) -> u32 { | ----------- ^^^ expected `u32`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression2 | price * quantity; | - help: remove this semicolon to return this valueIl compilatore dice che la funzione restituisce implicitamente () perché il corpo non ha un’espressione finale, e ti suggerisce di togliere il punto e virgola. Se vedi found (), controlla l’ultima riga della funzione. In Rust, l’ultima espressione di un blocco senza punto e virgola è il suo valore di ritorno. Aggiungere ; la trasforma in un’istruzione, quindi la funzione restituisce () (il tipo «niente»). Cancella il punto e virgola:
fn total_price(price: u32, quantity: u32) -> u32 { price * quantity // niente punto e virgola: questo valore viene restituito}
fn main() { println!("Totale: {}", total_price(250, 3));}Output:
Totale: 750Lo stesso succede dentro i blocchi if e i rami del match. Trovi di più in Funzioni.
Soluzione 2: converti tra String e &str
Sezione intitolata “Soluzione 2: converti tra String e &str”String è una stringa posseduta che può crescere. &str è una vista in prestito su un testo. Vedi Stringhe per tutta la storia. La conversione è breve in entrambe le direzioni:
- Da
&straString:.to_string()oppureString::from(...). - Da
Stringa&str: prendila in prestito con&(oppure chiama.as_str()).
Il secondo caso compare come expected &str, found String, di solito quando passi una String a una funzione che riceve un &str.
struct User { name: String,}
fn greet(name: &str) { println!("Ciao, {name}!");}
fn main() { // &str -> String: .to_string() oppure String::from() let user = User { name: "Ferris".to_string() };
// String -> &str: prendila in prestito con & (oppure chiama .as_str()) greet(&user.name); greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new(); tags.push(String::from("rust")); println!("{tags:?}");}Output:
Ciao, Ferris!Ciao, Ferris!["rust"]Soluzione 3: converti tra tipi numerici
Sezione intitolata “Soluzione 3: converti tra tipi numerici”error[E0308]: mismatched types --> src/main.rs:3:22 |3 | let count: i32 = names.len(); | --- ^^^^^^^^^^^ expected `i32`, found `usize` | | | expected due to thisQui Rust si aspettava un i32 (per via dell’annotazione) e ha trovato un usize. i32, i64, u32 e usize sono tutti tipi diversi, e Rust non li mescola. Lunghezze e indici sono sempre usize. Scegli una conversione:
.into()(oi64::from(x)) quando la conversione non può mai perdere dati, come dai32ai64.i64::try_from(x)(o.try_into()) quando il valore potrebbe non starci. Restituisce unResult, così decidi tu che cosa succede in caso di fallimento.asè il cast veloce. Non fallisce mai, ma taglia in silenzio i valori che non ci stanno, quindi usalo solo quando sai che il valore è piccolo.
fn average(total: i64, count: i64) -> i64 { total / count}
fn main() { let scores: Vec<i32> = vec![80, 95, 70]; let total: i32 = scores.iter().sum();
// da i32 a i64 ci sta sempre, quindi basta .into() let total: i64 = total.into();
// da usize a i64 potrebbe non starci, quindi try_from chiede prima let count = i64::try_from(scores.len()).unwrap();
println!("Media: {}", average(total, count));
// anche `as` funziona, ma taglia in silenzio i valori che non ci stanno let players = scores.len() as i32; println!("{players} giocatori");}Output:
Media: 813 giocatoriSpesso la soluzione più semplice è cambiare l’annotazione: let count: usize = names.len();. Le pagine Conversione di tipo (cast) e Conversioni approfondiscono.
Soluzione 4: avvolgi in Some, oppure estrai il valore dall’Option
Sezione intitolata “Soluzione 4: avvolgi in Some, oppure estrai il valore dall’Option”error[E0308]: mismatched types --> src/main.rs:3:27 |3 | let position: usize = names.iter().position(|n| *n == "Ben"); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Option<usize>` | | | expected due to thisRust si aspettava un usize e ha trovato un Option<usize>. Un Option<usize> non è un usize: è «forse un usize, forse niente». Questo errore va in entrambe le direzioni:
- Expected
T, foundOption<T>: devi gestire il caso «niente», conmatch,if letounwrap_or(default). - Expected
Option<T>, foundT: la tua funzione restituisce unOption, quindi avvolgi il valore:Some(10)invece di10.
fn find_discount(code: &str) -> Option<u32> { if code == "SAVE10" { return Some(10); // da T a Option<T>: avvolgilo in Some } None}
fn main() { let names = vec!["Ana", "Ben", "Cy"];
// da Option<T> a T: gestisci il caso «non trovato»... match names.iter().position(|n| *n == "Ben") { Some(i) => println!("Ben è in posizione {i}"), None => println!("Ben non c'è"), }
// ...oppure scegli un valore predefinito con unwrap_or let discount: u32 = find_discount("HELLO").unwrap_or(0); println!("Sconto: {discount}%");}Output:
Ben è in posizione 1Sconto: 0%Il compilatore potrebbe suggerirti .expect("REASON"). Funziona anche quello, ma fa crashare il programma quando il valore è None, quindi usalo solo quando None davvero non può capitare. Vedi Option.
Soluzione 5: fai in modo che ogni ramo di if/else e match abbia lo stesso tipo
Sezione intitolata “Soluzione 5: fai in modo che ogni ramo di if/else e match abbia lo stesso tipo”error[E0308]: `if` and `else` have incompatible types --> src/main.rs:3:52 |3 | let label = if stock > 0 { "in stock" } else { stock }; | ---------- ^^^^^ expected `&str`, found integer | | | expected because of thisIl compilatore dice che if ed else hanno tipi incompatibili. Quando un if o un match produce un valore, Rust deve conoscerne l’unico tipo. Quindi ogni ramo deve produrre lo stesso tipo. Il primo ramo stabilisce l’aspettativa, e l’errore indica il ramo in disaccordo. Per match il messaggio dice «match arms have incompatible types». Metti d’accordo il ramo diverso:
fn status_text(code: u16) -> String { match code { 200 => "OK".to_string(), 404 => "Not Found".to_string(), _ => format!("Codice sconosciuto {code}"), // ogni ramo è una String }}
fn main() { let stock = 0; let label = if stock > 0 { "disponibile" } else { "esaurito" }; // entrambi &str println!("{label}");
println!("{}", status_text(404)); println!("{}", status_text(418));}Output:
esauritoNot FoundCodice sconosciuto 418Qui un ramo aveva bisogno di format!, che crea una String, quindi anche gli altri rami sono diventati String. Vedi If…else e Match.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”- Leggere l’input dell’utente.
let age: u32 = input.trim();dà «expectedu32, found&str». Un testo non è un numero. Soluzione: convertilo:let age: u32 = input.trim().parse().unwrap();(oppure gestisci l’errore, vedi Result). - Inserire letterali in un
Vec<String>.tags.push("rust")dà «expectedString, found&str». Soluzione:tags.push("rust".to_string()), oppure usa unVec<&str>se le stringhe sono tutte letterali. - Calcoli con tipi numerici misti.
total + n, dovetotalèi64enèi32, dà E0308 (e spesso anche E0277 accanto). Soluzione: converti prima uno dei due:total + i64::from(n).
Errori correlati
Sezione intitolata “Errori correlati”- E0277: trait bound not satisfied: compare spesso accanto a E0308, per esempio quando sommi un
i64a uni32. - E0599: no method found: hai chiamato un metodo che non esiste su questo tipo, spesso perché il valore ha un tipo diverso da quello che pensavi.
- E0425: cannot find value in this scope: un nome che Rust non conosce affatto.
- Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Una funzione dichiara -> i32, ma l'errore dice "expected i32, found ()". Qual è la causa più probabile?
2. Una funzione riceve un &str e tu hai let name = String::from("Ana");. Come lo passi?
3. Quale conversione non può mai perdere dati in silenzio?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0308 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- Functions with Return Values — The Rust Book (in inglese): perché un punto e virgola finale cambia quello che restituisce una funzione.
- Storing UTF-8 Text with Strings — The Rust Book (in inglese): che rapporto c’è tra
Stringe&stre come convertire dall’una all’altro. - Conversion — Rust by Example (in inglese):
From,Into,TryFromeTryIntocon brevi esempi. - Option — libreria standard di Rust (in inglese): tutti i metodi per estrarre un valore da un
Option, comeunwrap_oremap.