Erreur Rust E0252 : the name is defined multiple times (deux imports)
E0252 veut dire que deux lignes use importent le même nom. Le cas d’école : use std::fmt::Result; plus use std::io::Result;, deux types différents qui s’appellent tous les deux Result. On le voit aussi avec fmt::Error et io::Error, ou avec les deux traits Write. C’est une erreur très courante, et facile à corriger, en général en changeant une ou deux lignes use. rustc 1.97 l’affiche toujours sous son propre code, E0252. Cette page fait partie du guide des erreurs du compilateur Rust.
Le message d’erreur
Section intitulée « Le message d’erreur »error[E0252]: the name `Result` is defined multiple times --> src/main.rs:2:5 |1 | use std::fmt::Result; | ---------------- previous import of the type `Result` here2 | use std::io::Result; // ❌ un deuxième `Result` | ^^^^^^^^^^^^^^^ `Result` reimported here | = note: `Result` must be defined only once in the type namespace of this modulehelp: you can use `as` to change the binding name of the import |2 | use std::io::Result as OtherResult; // ❌ un deuxième `Result` | ++++++++++++++Le compilateur vous dit : « le nom Result est défini plusieurs fois ». La section « Ce que ça veut dire » ci-dessous traduit chaque ligne.
Voici le programme qui la provoque :
use std::fmt::Result;use std::io::Result; // ❌ un deuxième `Result`
struct Temperature { celsius: i32,}
impl std::fmt::Display for Temperature { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result { write!(f, "{} °C", self.celsius) }}
fn parse_temperature(text: &str) -> Result<Temperature> { match text.trim().parse() { Ok(celsius) => Ok(Temperature { celsius }), Err(_) => Err(std::io::Error::other("not a number")), }}
fn main() { match parse_temperature("21") { Ok(t) => println!("{}", t), Err(e) => println!("error: {}", e), }}rustc affiche quatre autres erreurs après celle-ci : E0107 (« type alias takes 0 generic arguments », l’alias de type ne prend aucun argument générique), deux E0308 (types incompatibles) et une E0277. Ne les pourchassez pas. Rust a gardé le premier import, donc chaque Result du fichier désigne maintenant fmt::Result, et parse_temperature n’a plus de sens. Corrigez E0252, et elles disparaissent toutes.
Ce que ça veut dire
Section intitulée « Ce que ça veut dire »rustc pointe deux lignes use :
- La ligne
---(ligne 1) : « previous import of the typeResulthere » (import précédent du typeResult). Celui-ci est arrivé en premier, c’est donc lui que Rust garde. - La ligne
^^^(ligne 2) : «Resultreimported here » (Resultimporté une seconde fois ici). Le second import essaie de prendre le même nom. - Le
help:propose de renommer le second import avecas.
std::fmt::Result et std::io::Result sont vraiment différents. fmt::Result est un simple Result<(), fmt::Error>, utilisé par Display et Debug. io::Result<T> est un Result<T, io::Error>, utilisé pour les fichiers et le réseau. Même nom, rôles différents.
C’est comme enregistrer deux contacts sous le nom « Maman » dans votre téléphone : votre mère et la mère de votre conjoint. Quand vous touchez « Maman », le téléphone doit savoir qui appeler. Vous ajoutez un mot à l’un des deux, ou vous les enregistrez sous « Maman (Ana) » et « Maman (Lee) ».
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »Dans un module, chaque nom désigne exactement un élément. Une ligne use ne copie rien : elle ajoute un nom court pour un élément qui vit ailleurs. Deux lignes use qui ajoutent le même nom court rendraient ambigu chaque Result qui suit, alors Rust s’arrête à l’import au lieu de deviner à chaque utilisation.
E0252 fait partie de trois erreurs sœurs avec le même titre : E0255, c’est une ligne use contre votre propre élément, et E0428, deux de vos propres éléments. La règle derrière les trois : « un nom, une seule chose, par module ». Voir use : raccourcir les chemins.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : importer les modules et écrire fmt:: et io::
Section intitulée « Solution 1 : importer les modules et écrire fmt:: et io:: »Choisissez-la la plupart du temps, et toujours pour Result et Error. Importez les modules parents, puis écrivez le nom du module devant. Chaque utilisation dit alors de quel Result il s’agit. Le Rust Book recommande ce style justement pour ce cas :
use std::fmt;use std::io;
struct Temperature { celsius: i32,}
impl fmt::Display for Temperature { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} °C", self.celsius) }}
fn parse_temperature(text: &str) -> io::Result<Temperature> { match text.trim().parse() { Ok(celsius) => Ok(Temperature { celsius }), Err(_) => Err(io::Error::other("ce n'est pas un nombre")), }}
fn main() { for input in ["21", " -4 ", "tiède"] { match parse_temperature(input) { Ok(t) => println!("{}", t), Err(e) => println!("erreur : {}", e), } }}Sortie :
21 °C-4 °Cerreur : ce n'est pas un nombreEn bonus : le Result<T, E> ordinaire du prélude reste disponible, sans aucun import.
Solution 2 : renommer un des imports avec as
Section intitulée « Solution 2 : renommer un des imports avec as »Choisissez-la quand vous utilisez beaucoup l’un des deux types et que vous voulez un nom court pour lui. C’est la suggestion de rustc. Choisissez un nom qui dit d’où il vient, comme IoResult, plutôt que OtherResult :
use std::fmt::Result;use std::io::Result as IoResult;
struct Temperature { celsius: i32,}
impl std::fmt::Display for Temperature { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result { write!(f, "{} °C", self.celsius) }}
fn parse_temperature(text: &str) -> IoResult<Temperature> { match text.trim().parse() { Ok(celsius) => Ok(Temperature { celsius }), Err(_) => Err(std::io::Error::other("ce n'est pas un nombre")), }}
fn main() { match parse_temperature("21") { Ok(t) => println!("{}", t), Err(e) => println!("erreur : {}", e), }}Sortie :
21 °CUn simple Result qui veut dire en secret fmt::Result peut surprendre la prochaine personne qui lit le code, donc la solution 1 est en général plus claire. as est surtout utile pour les noms longs, comme use std::collections::BTreeMap as Map;.
Solution 3 : importer un trait avec as _
Section intitulée « Solution 3 : importer un trait avec as _ »Choisissez-la pour les traits que vous n’importez que pour appeler leurs méthodes. Les deux traits Write sont l’exemple classique : fmt::Write permet d’utiliser write! dans une String, io::Write permet d’utiliser write! vers la sortie standard ou un fichier. Les importer tous les deux par leur nom crée un conflit :
error[E0252]: the name `Write` is defined multiple times --> src/main.rs:2:5 |1 | use std::fmt::Write; | --------------- previous import of the trait `Write` here2 | use std::io::Write; // ❌ un deuxième `Write` | ^^^^^^^^^^^^^^ `Write` reimported here | = note: `Write` must be defined only once in the type namespace of this modulehelp: you can use `as` to change the binding name of the import |2 | use std::io::Write as OtherWrite; // ❌ un deuxième `Write` | +++++++++++++Le programme qui ne compile pas :
use std::fmt::Write;use std::io::Write; // ❌ un deuxième `Write`
fn main() { let mut report = String::new(); write!(report, "{} tests passed", 12).unwrap();
let mut out = std::io::stdout(); writeln!(out, "{}", report).unwrap();}Ici, vous n’écrivez jamais Write vous-même : vous avez seulement besoin de ses méthodes. as _ importe un trait sans lui donner de nom, donc aucun conflit n’est possible, et les deux séries de méthodes fonctionnent quand même :
use std::fmt::Write as _;use std::io::Write as _;
fn main() { let mut report = String::new(); write!(report, "{} tests réussis", 12).unwrap();
let mut out = std::io::stdout(); writeln!(out, "{}", report).unwrap();}Sortie :
12 tests réussisSi vous devez nommer l’un des deux, par exemple dans fn log(w: &mut impl io::Write), combinez avec la solution 1 : use std::io; et écrivez io::Write. Voir Les traits.
Solution 4 : supprimer un import en double du même élément
Section intitulée « Solution 4 : supprimer un import en double du même élément »Choisissez-la quand les deux lignes importent exactement la même chose, ce qui arrive souvent après une fusion de code, ou quand votre éditeur ajoute un import que vous aviez déjà :
error[E0252]: the name `HashMap` is defined multiple times --> src/main.rs:2:24 |1 | use std::collections::HashMap; | ------------------------- previous import of the type `HashMap` here2 | use std::collections::{HashMap, HashSet}; // ❌ encore `HashMap` | ^^^^^^^-- | | | `HashMap` reimported here | help: remove unnecessary importLe programme qui ne compile pas :
use std::collections::HashMap;use std::collections::{HashMap, HashSet}; // ❌ encore `HashMap`
fn main() { let mut stock: HashMap<&str, u32> = HashMap::new(); stock.insert("apples", 3);
let mut sold_out: HashSet<&str> = HashSet::new(); sold_out.insert("pears");
println!("apples: {}, sold out: {}", stock["apples"], sold_out.len());}Remarquez que l’aide est différente : pas de as, juste « remove unnecessary import » (supprimez l’import inutile). Gardez une seule ligne :
use std::collections::{HashMap, HashSet};
fn main() { let mut stock: HashMap<&str, u32> = HashMap::new(); stock.insert("pommes", 3);
let mut sold_out: HashSet<&str> = HashSet::new(); sold_out.insert("poires");
println!("pommes : {}, épuisés : {}", stock["pommes"], sold_out.len());}Sortie :
pommes : 3, épuisés : 1Voir Les HashMap et Les HashSet.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Error venant de deux endroits. use std::fmt::Error; et use std::io::Error;, ou use std::error::Error; à côté de use anyhow::Error;. Correction : use std::io; et écrivez io::Error, ou use anyhow::Error as AnyError;. Voir anyhow et thiserror.
Deux crates avec le même nom de type. use chrono::Duration; et use std::time::Duration; forment une paire fréquente dans les projets qui manipulent des dates. Correction : gardez un nom court pour l’un, et écrivez le module de l’autre, comme chrono::Duration. Voir Les dates et heures.
Les imports glob. use std::fmt::*; plus use std::io::*; ne donne pas d’E0252 sur les lignes use. Le problème arrive plus tard, au premier Result, sous la forme d’une erreur sans code : error: `Result` is ambiguous (Result est ambigu). Correction : importez explicitement les noms dont vous avez besoin, comme dans la solution 1.
Erreurs liées
Section intitulée « Erreurs liées »- E0255 : une ligne
useentre en conflit avec votre propre struct, enum ou fonction du même nom. - E0428 : deux de vos propres éléments portent le même nom, souvent parce que vous avez essayé de surcharger une fonction.
- E0432 : import non résolu, quand la ligne
usepointe vers quelque chose qui n’existe pas. - E0034 : deux traits visibles donnent à un type des méthodes du même nom.
- E0308 : types incompatibles, l’une des erreurs qui suivent un conflit de
Result. - Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Vous avez besoin de fmt::Result et de io::Result dans le même fichier. Quels imports sont le choix propre habituel ?
2. Vous avez besoin des méthodes de std::fmt::Write et de std::io::Write, mais vous n'écrivez jamais vous-même le nom des traits. Qu'est-ce qui marche ?
3. rustc dit que HashMap est « reimported here » et propose remove unnecessary import. Que s'est-il passé ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0252 — Rust error codes index (en anglais) : l’explication officielle, avec la correction par
aset celle par le chemin du module. - Providing New Names with the
asKeyword — The Rust Book (en anglais) : l’exemplefmt::Resultetio::Result, résolu de deux façons. - Underscore imports — The Rust Reference (en anglais) : comment
use Trait as _;importe les méthodes sans donner de nom. - The
usedeclaration — Rust by Example (en anglais) : de courts exemples exécutables pour importer et renommer. - Namespaces — The Rust Reference (en anglais) : pourquoi les traits et les types partagent l’espace de noms des types dont parle la note de l’erreur.