Erreur Rust E0284 : type annotations needed (type manquant pour parse())
E0284 veut presque toujours dire une seule chose : vous avez appelé .parse(), et Rust ne sait pas en quoi convertir le texte. La note cannot satisfy `<_ as FromStr>::Err == _` a l’air effrayante, mais la correction tient en général en quelques caractères, comme : u32 ou ::<u32>. Cette page vous donne d’abord la solution, puis décode cette note pour qu’elle cesse de vous faire peur. Pour les autres erreurs de cette série, consultez les erreurs du compilateur Rust.
Le message d’erreur
Section intitulée « Le message d’erreur »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(); | ++++++++++++Le compilateur vous dit : « annotations de type nécessaires ». L’étiquette sous parse signifie « le type doit être connu à cet endroit », et l’aide propose de donner un type explicite à age.
Voici le programme qui la provoque :
fn main() { let age = "42".parse().unwrap(); println!("Next year you'll be {}", age + 1);}Vous verrez la même note avec .expect(...) au lieu de .unwrap(), avec match "42".parse() { ... }, et avec un .parse() à l’intérieur d’une closure. Quand le résultat de .parse() n’est pas rangé dans une variable, rustc pointe parse lui-même et suggère parse::</* Type */>().
Ce que ça veut dire
Section intitulée « Ce que ça veut dire »Les marques sont la partie facile :
^^^sousage: la variable dont le type est inconnu.-----sousparse: « type must be known at this point » (le type doit être connu à cet endroit). C’est là que Rust s’est retrouvé bloqué.- L’aide : « consider giving
agean explicit type » (pensez à donner un type explicite àage). Remplacez/* Type */par un vrai type, commeu32.
Maintenant, la note, morceau par morceau. Dans les messages du compilateur, _ veut dire « un type que je ne connais pas encore » :
| Morceau | En clair |
|---|---|
<_ as FromStr> |
« le type vers lequel vous convertissez (je ne le connais pas encore) » |
::Err |
« … et le type d’erreur qui va avec » |
== _ |
« doit être égal à un autre type que je ne connais pas non plus » |
La note entière dit donc : « Je n’arrive même pas à savoir quel genre d’erreur parse pourrait renvoyer, parce que je ne sais pas en quoi vous convertissez. »
Imaginez que vous tendez une lettre à un traducteur en lui disant « traduisez-la ». En anglais ? En japonais ? Tant que vous ne l’avez pas dit, il ne peut pas commencer, et il ne peut même pas vous dire quelles difficultés il risque de rencontrer.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »Voici la signature de parse, un peu simplifiée :
fn parse<F: FromStr>(&self) -> Result<F, F::Err>F est le type que vous voulez obtenir, et F::Err est son type d’erreur. Chaque type cible apporte le sien : convertir en i32 peut échouer avec une ParseIntError, en f64 avec une ParseFloatError, en bool avec une ParseBoolError. Rust a besoin de F pour savoir ce que parse renvoie, tout simplement.
Ce choix change aussi ce qui se passe à l’exécution. "42" se convertit sans problème en u8, en i32 ou en f64. Mais "300" échoue en u8 (trop grand), et "4.5" échoue en i32 (ce n’est pas un nombre entier). Rust ne devine pas, parce qu’une mauvaise supposition deviendrait un bug qui n’apparaîtrait qu’avec certaines entrées. La leçon Les conversions montre comment fonctionne FromStr, et comment l’ajouter à vos propres types.
La façon dont Rust devine les types tout seul est expliquée sur la page de E0282. La raison pour laquelle il existe trois codes différents (E0282, E0283, E0284) pour « type annotations needed » est expliquée sur la page de E0283. En bref : E0284 est la variante où la règle bloquée porte sur un type qui appartient au type inconnu (ici F::Err). Pour vous, la correction est la même : nommez le type.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : annoter la variable
Section intitulée « Solution 1 : annoter la variable »Choisissez-la dans la plupart des cas. C’est le style qu’utilise le Rust Book, et il indique clairement ce que le texte doit contenir :
fn main() { let age: u32 = "42".parse().unwrap(); let price: f64 = "4.99".parse().unwrap(); let port: u16 = "8080".parse().expect("le port doit être un nombre");
println!("L'an prochain, vous aurez {} ans", age + 1); println!("Deux cafés : {:.2}", price * 2.0); println!("En écoute sur le port {port}");}Sortie :
L'an prochain, vous aurez 43 ansDeux cafés : 9.98En écoute sur le port 8080Choisissez le type qui correspond au sens du texte : u16 pour un port, u8 pour un pourcentage, f64 pour un prix. Un type trop petit transforme une entrée valide en erreur : dans le doute, prenez un type plus large comme i64 ou u32. (Notez que Rust écrit les nombres à virgule avec un point : "4.99", pas "4,99".)
Solution 2 : utiliser le turbofish parse::<T>()
Section intitulée « Solution 2 : utiliser le turbofish parse::<T>() »Choisissez-la quand il n’y a pas de variable à annoter : dans une closure, au milieu d’une chaîne d’itérateurs, ou dans une expression comme ... * 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!("Total : {total}"); println!("Le double : {doubled}");}Sortie :
Total : 15Le double : 42Mettez le turbofish sur parse lui-même. Dans une chaîne, annoter seulement le résultat final ne suffit souvent pas ; voyez le premier point de la section « Où vous le verrez souvent » plus bas.
Solution 3 : nommer le type et bien gérer l’erreur
Section intitulée « Solution 3 : nommer le type et bien gérer l’erreur »Choisissez-la pour de vraies entrées (fichiers, arguments de la ligne de commande, formulaires web), où .unwrap() ferait planter votre programme sur un texte invalide. Nommez le type sur parse et faites un match sur le Result. Remarquez comme le type décide quelles entrées réussissent :
fn main() { for input in ["42", "300", "abc"] { match input.parse::<u8>() { Ok(n) => println!("{input} : obtenu {n}"), Err(e) => println!("{input} : {e}"), } }
let rate: Result<f64, _> = "0.25".parse(); println!("{:?}", rate);}Sortie :
42 : obtenu 42300 : number too large to fit in target typeabc : invalid digit found in stringOk(0.25)Les messages d’erreur viennent de la bibliothèque standard et restent en anglais : « number too large to fit in target type » veut dire « nombre trop grand pour le type cible » (300 ne tient pas dans un u8), et « invalid digit found in string » veut dire « chiffre invalide dans la chaîne ».
Dans Result<f64, _>, vous ne nommez que le type de la valeur. Le _ laisse Rust remplir le type d’erreur (ParseFloatError), c’est-à-dire exactement la partie que réclamait la note. Pour en savoir plus, voir Result.
Solution 4 : utiliser la valeur là où son type est fixé
Section intitulée « Solution 4 : utiliser la valeur là où son type est fixé »Choisissez-la quand la valeur convertie va dans un champ de struct ou un paramètre de fonction dont le type est connu. Rust lit toute la fonction, et cette utilisation plus loin répond à la question :
struct Config { port: u16, workers: u8,}
fn greet(age: u32) { println!("Bienvenue ! Vous avez {age} ans.");}
fn main() { let port = "8080".parse().unwrap(); // u16, d'après Config let workers = "4".parse().unwrap(); // u8, d'après Config let config = Config { port, workers }; println!("Port {} avec {} workers", config.port, config.workers);
let age = "42".parse().unwrap(); // u32, d'après greet greet(age);}Sortie :
Port 8080 avec 4 workersBienvenue ! Vous avez 42 ans.Toutes les utilisations ne comptent pas, et c’est pour ça que c’est la solution 4. Les calculs comme age + 1 et les comparaisons comme age >= limit donnent encore E0284, même quand limit est un u32 : les opérateurs fonctionnent entre beaucoup de paires de types, ils ne désignent donc pas un seul type précis. Et appeler une méthode, comme age.pow(2), transforme l’erreur en E0282. Dans le doute, utilisez la solution 1.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Convertir dans un map, puis sum(). let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); donne encore E0284, alors que total a un type. Un u32 peut être la somme de valeurs u32 ou de valeurs &u32 : l’annotation ne dit donc pas à Rust ce qu’est chaque élément. Correction : s.parse::<u32>(). (Collecter dans un Vec<i32> fonctionne, en revanche, parce qu’un Vec<i32> ne peut contenir que des i32.)
Collecter des résultats de parse dans un Result. let nums: Result<Vec<_>, _> = text.split(' ').map(|x| x.parse()).collect(); donne E0284 : le _ de Vec<_> laisse le type des éléments ouvert. Correction : Result<Vec<i32>, _>. Ce motif bien pratique s’arrête au premier élément invalide et vous donne son erreur.
Lire une saisie de l’utilisateur. let guess = line.trim().parse().unwrap(); dans un jeu de devinettes ou un outil en ligne de commande. Correction : let guess: u32 = ..., exactement comme dans le Rust Book.
Avec ?, l’erreur est plus étrange. let port = text.parse()?; ne donne pas E0284. Dans l’édition 2021, Rust se rabat sur le type () et signale E0277 : the trait bound `(): FromStr` is not satisfied (la contrainte de trait (): FromStr n’est pas satisfaite ; l’édition 2024 écrit ! au lieu de ()). Même correction : let port: u16 = text.parse()?;.
Erreurs liées
Section intitulée « Erreurs liées »- E0282 : type annotations needed (annotation de type requise), quand il n’y a aucun indice (ou que le type est nécessaire pour un appel de méthode comme
age.pow(2)). - E0283 : type annotations needed après
.sum(),.collect()ou.into(), quand beaucoup de types conviennent. Cette page explique aussi pourquoi il y a trois codes différents. - E0790 :
FromStr::from_str("42")ouDefault::default()appelés sans préciser le type. - E0277 : ce que donne
.parse()?sans type, et ce que vous obtenez quand le type choisi ne peut pas être construit à partir de texte. - E0599 : méthode introuvable, par exemple quand vous appelez
.parse()sur quelque chose qui n’est pas du texte, comme une variableu32(convertissez-la plutôt avecu8::try_from(n)). - Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Que veut dire cannot satisfy <_ as FromStr>::Err == _, en clair ?
2. Quelle ligne corrige let port = "8080".parse().unwrap(); ?
3. let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); donne encore E0284. Où faut-il mettre le type ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0284 — Rust error codes index (en anglais) : l’explication officielle de cette erreur.
str::parse— documentation de la bibliothèque standard (en anglais) : la signature de la méthode, avec le style annotation et le style turbofish.FromStr— documentation de la bibliothèque standard (en anglais) : le trait derrièreparse, avec son typeErret la façon de l’implémenter.- Programming a Guessing Game — The Rust Book (en anglais) : là où
let guess: u32 = guess.trim().parse()apparaît pour la première fois, expliqué pas à pas. - Data Types — The Rust Book (en anglais) : pourquoi
"42".parse()a besoin d’une annotation de type, avec la vraie erreur du compilateur.