Salta ai contenuti

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

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);
}

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.

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.

Ogni soluzione qui sotto parte dalla forma dell’errore, così puoi saltare a quella che corrisponde al tuo.

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` expression
2 | price * quantity;
| - help: remove this semicolon to return this value

Il 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: 750
▶ Provalo tu

Lo stesso succede dentro i blocchi if e i rami del match. Trovi di più in Funzioni.

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 &str a String: .to_string() oppure String::from(...).
  • Da String a &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"]
▶ Provalo tu
error[E0308]: mismatched types
--> src/main.rs:3:22
|
3 | let count: i32 = names.len();
| --- ^^^^^^^^^^^ expected `i32`, found `usize`
| |
| expected due to this

Qui 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() (o i64::from(x)) quando la conversione non può mai perdere dati, come da i32 a i64.
  • i64::try_from(x) (o .try_into()) quando il valore potrebbe non starci. Restituisce un Result, 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: 81
3 giocatori
▶ Provalo tu

Spesso 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 this

Rust 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, found Option<T>: devi gestire il caso «niente», con match, if let o unwrap_or(default).
  • Expected Option<T>, found T: la tua funzione restituisce un Option, quindi avvolgi il valore: Some(10) invece di 10.
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 1
Sconto: 0%
▶ Provalo tu

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 this

Il 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:

esaurito
Not Found
Codice sconosciuto 418
▶ Provalo tu

Qui un ramo aveva bisogno di format!, che crea una String, quindi anche gli altri rami sono diventati String. Vedi If…else e Match.

  • Leggere l’input dell’utente. let age: u32 = input.trim(); dà «expected u32, 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à «expected String, found &str». Soluzione: tags.push("rust".to_string()), oppure usa un Vec<&str> se le stringhe sono tutte letterali.
  • Calcoli con tipi numerici misti. total + n, dove total è i64 e n è i32, dà E0308 (e spesso anche E0277 accanto). Soluzione: converti prima uno dei due: total + i64::from(n).

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