Erreur Rust E0308 : mismatched types (types incompatibles)
L’erreur E0308 veut dire que Rust attendait une valeur d’un certain type, mais que vous lui avez donné une valeur d’un autre type. C’est sans doute l’erreur que vous verrez le plus souvent en Rust. Heureusement, le message indique presque toujours les deux types, donc elle se corrige en général très vite. (Vous cherchez une autre erreur ? Consultez la liste des erreurs du compilateur.)
Le message d’erreur
Section intitulée « Le message d’erreur »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() }; | ++++++++++++Le compilateur dit : « types incompatibles : String attendu, &str trouvé », et il propose une méthode de conversion.
Voici le programme qui la provoque :
struct User { name: String,}
fn main() { let user = User { name: "Ferris" }; println!("{}", user.name);}Ce que ça veut dire
Section intitulée « Ce que ça veut dire »Tous les messages E0308 ont la même forme : expected A, found B (« A attendu, B trouvé »).
- Expected (attendu), c’est le type dont Rust a besoin à cet endroit. Il vient d’ailleurs : une annotation de type, le paramètre ou le type de retour d’une fonction, un champ de struct, ou une autre branche d’un
if/match. - Found (trouvé), c’est le type de la valeur que vous avez réellement écrite là.
Dans l’exemple, le champ name a été déclaré comme String, donc c’est ce que Rust attend. Mais "Ferris" est une chaîne littérale, dont le type est &str. Les deux sont liés, mais ce ne sont pas le même type.
Regardez aussi les lignes help:. Pour E0308, le compilateur propose souvent la correction exacte, comme .to_string() ci-dessus.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »Rust ne convertit jamais un type en un autre en silence. Dans certains langages, un nombre devient discrètement une chaîne, ou un grand nombre est tronqué pour rentrer dans un type plus petit. Ces conversions cachées provoquent de vrais bugs : des données perdues, des résultats faux, des plantages très loin de l’erreur d’origine.
Rust rend donc chaque conversion visible dans le code. Ça demande un peu plus de frappe, mais quand vous relisez le code plus tard, vous voyez exactement où une valeur change de type et si cette étape peut échouer. Les types eux-mêmes sont présentés dans Les types de données.
Comment corriger
Section intitulée « Comment corriger »Chaque solution ci-dessous commence par la forme de l’erreur : allez directement à celle qui ressemble à la vôtre.
Solution 1 : enlever le point-virgule final
Section intitulée « Solution 1 : enlever le point-virgule final »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 valueSi vous voyez found (), regardez la dernière ligne de la fonction. En Rust, la dernière expression d’un bloc sans point-virgule est sa valeur de retour. Ajouter ; la transforme en instruction, et la fonction renvoie alors () (le type « rien ») à la place. Le compilateur le dit d’ailleurs : « supprimez ce point-virgule pour renvoyer cette valeur ». Effacez-le :
fn total_price(price: u32, quantity: u32) -> u32 { price * quantity // pas de point-virgule : cette valeur est renvoyée}
fn main() { println!("Total : {}", total_price(250, 3));}Sortie :
Total : 750La même chose arrive dans les blocs if et les branches de match. Plus de détails dans Les fonctions.
Solution 2 : convertir entre String et &str
Section intitulée « Solution 2 : convertir entre String et &str »String est une chaîne qui vous appartient et qui peut grandir. &str est une vue empruntée sur un texte. Tous les détails sont dans Les chaînes de caractères. La conversion est courte dans les deux sens :
- de
&strversString:.to_string()ouString::from(...); - de
Stringvers&str: empruntez-la avec&(ou appelez.as_str()).
Le deuxième cas apparaît sous la forme expected &str, found String, en général quand vous passez une String à une fonction qui prend un &str.
struct User { name: String,}
fn greet(name: &str) { println!("Bonjour, {name} !");}
fn main() { // &str -> String : .to_string() ou String::from() let user = User { name: "Ferris".to_string() };
// String -> &str : empruntez-la avec & (ou appelez .as_str()) greet(&user.name); greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new(); tags.push(String::from("rust")); println!("{tags:?}");}Sortie :
Bonjour, Ferris !Bonjour, Ferris !["rust"]Solution 3 : convertir entre types de nombres
Section intitulée « Solution 3 : convertir entre types de nombres »error[E0308]: mismatched types --> src/main.rs:3:22 |3 | let count: i32 = names.len(); | --- ^^^^^^^^^^^ expected `i32`, found `usize` | | | expected due to thisi32, i64, u32 et usize sont tous des types différents, et Rust ne les mélange pas. Les longueurs et les indices sont toujours des usize. Choisissez une conversion :
.into()(oui64::from(x)) quand la conversion ne peut jamais perdre de données, par exemple dei32versi64.i64::try_from(x)(ou.try_into()) quand la valeur risque de ne pas rentrer. Elle renvoie unResult, et c’est vous qui décidez quoi faire en cas d’échec.asest la conversion rapide. Elle n’échoue jamais, mais elle tronque en silence les valeurs trop grandes : utilisez-la seulement quand vous savez que la valeur est petite.
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();
// i32 -> i64 rentre toujours, donc .into() suffit let total: i64 = total.into();
// usize -> i64 pourrait ne pas rentrer, donc try_from vérifie d'abord let count = i64::try_from(scores.len()).unwrap();
println!("Moyenne : {}", average(total, count));
// `as` marche aussi, mais tronque en silence les valeurs trop grandes let players = scores.len() as i32; println!("{players} joueurs");}Sortie :
Moyenne : 813 joueursSouvent, la correction la plus simple est de changer l’annotation : let count: usize = names.len();. Les pages Conversion de types et Les conversions vont plus loin.
Solution 4 : envelopper dans Some, ou sortir la valeur de l’Option
Section intitulée « Solution 4 : envelopper dans Some, ou sortir la valeur de l’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 thisUne Option<usize> n’est pas un usize. C’est « peut-être un usize, peut-être rien ». Cette erreur existe dans les deux sens :
- Expected
T, foundOption<T>: vous devez traiter le cas « rien », avecmatch,if letouunwrap_or(valeur_par_défaut). - Expected
Option<T>, foundT: votre fonction renvoie uneOption, donc enveloppez la valeur :Some(10)au lieu de10.
fn find_discount(code: &str) -> Option<u32> { if code == "SAVE10" { return Some(10); // T -> Option<T> : on l'enveloppe dans Some } None}
fn main() { let names = vec!["Ana", "Ben", "Cy"];
// Option<T> -> T : on traite le cas « rien trouvé »... match names.iter().position(|n| *n == "Ben") { Some(i) => println!("Ben est à la position {i}"), None => println!("Ben n'est pas là"), }
// ...ou on choisit une valeur par défaut avec unwrap_or let discount: u32 = find_discount("HELLO").unwrap_or(0); println!("Réduction : {discount} %");}Sortie :
Ben est à la position 1Réduction : 0 %Le compilateur peut vous proposer .expect("REASON"). Ça marche aussi, mais le programme plante si la valeur est None : ne l’utilisez que quand None est vraiment impossible. Voir Option.
Solution 5 : donner le même type à toutes les branches d’un if/else ou d’un match
Section intitulée « Solution 5 : donner le même type à toutes les branches d’un if/else ou d’un match »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 thisIci, le message dit que « if et else ont des types incompatibles ». Quand un if ou un match produit une valeur, Rust doit connaître son type, et il n’y en a qu’un. Toutes les branches doivent donc produire le même type. La première branche fixe ce qui est attendu, et l’erreur montre la branche qui n’est pas d’accord. Pour match, le message dit « match arms have incompatible types ». Mettez la branche fautive au diapason :
fn status_text(code: u16) -> String { match code { 200 => "OK".to_string(), 404 => "Not Found".to_string(), _ => format!("Code inconnu {code}"), // chaque branche est une String }}
fn main() { let stock = 0; let label = if stock > 0 { "en stock" } else { "épuisé" }; // deux &str println!("{label}");
println!("{}", status_text(404)); println!("{}", status_text(418));}Sortie :
épuiséNot FoundCode inconnu 418Ici, une branche avait besoin de format!, qui crée une String, donc les autres branches sont devenues des String elles aussi. (« OK » et « Not Found » restent en anglais : ce sont les libellés standard des codes HTTP.) Voir if/else et match.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »- En lisant une saisie de l’utilisateur.
let age: u32 = input.trim();donne « expectedu32, found&str». Du texte n’est pas un nombre. Correction : convertissez-le :let age: u32 = input.trim().parse().unwrap();(ou gérez l’erreur, voir Result). - En ajoutant des littéraux dans un
Vec<String>.tags.push("rust")donne « expectedString, found&str». Correction :tags.push("rust".to_string()), ou utilisez unVec<&str>si toutes les chaînes sont des littéraux. - En calculant avec des types de nombres différents.
total + noùtotalest uni64etnuni32donne E0308 (et souvent E0277 juste à côté). Correction : convertissez d’abord un des deux côtés :total + i64::from(n).
Erreurs liées
Section intitulée « Erreurs liées »- E0277 : trait bound not satisfied : apparaît souvent à côté d’E0308, par exemple quand vous additionnez un
i64et uni32. - E0599 : no method found : vous appelez une méthode qui n’existe pas sur ce type, souvent parce que la valeur n’a pas le type que vous pensiez.
- E0425 : cannot find value in this scope : un nom que Rust ne connaît pas du tout.
- Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Une fonction indique -> i32, mais l'erreur dit "expected i32, found ()". Quelle est la cause la plus probable ?
2. Une fonction prend un &str et vous avez let name = String::from("Ana");. Comment la lui passer ?
3. Quelle conversion ne peut jamais perdre de données en silence ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0308 — Rust error codes index (en anglais) : l’explication officielle de cette erreur.
- Functions with Return Values — The Rust Book (en anglais) : pourquoi un point-virgule final change ce que renvoie une fonction.
- Storing UTF-8 Text with Strings — The Rust Book (en anglais) : le lien entre
Stringet&str, et comment passer de l’un à l’autre. - Conversion — Rust by Example (en anglais) :
From,Into,TryFrometTryIntoavec de courts exemples. - Option — documentation de la bibliothèque standard (en anglais) : toutes les méthodes pour sortir une valeur d’une
Option, commeunwrap_oretmap.