Erreur Rust E0592 : duplicate definitions with name X (méthode en double)
E0592 veut dire que le même type a deux méthodes, fonctions associées ou constantes du même nom, écrites dans des blocs impl. Un type peut avoir autant de blocs impl que vous voulez, mais ils partagent tous une seule liste de noms, et chaque nom ne peut servir qu’une fois. L’erreur vient en général d’un copier-coller, d’un merge, ou d’un deuxième bloc impl dans un autre fichier. La correction : en garder un, ou donner un nouveau nom à l’autre. Cette page fait partie du guide des erreurs du compilateur Rust.
Le message d’erreur
Section intitulée « Le message d’erreur »error[E0592]: duplicate definitions with name `reset` --> src/main.rs:10:5 |10 | fn reset(&mut self) { | ^^^^^^^^^^^^^^^^^^^ duplicate definitions for `reset`...16 | fn reset(&mut self) { // ❌ `reset` est déjà défini plus haut | ------------------- other definition for `reset`Le compilateur vous dit : « définitions en double pour le nom reset ». La ligne --- marque l’« other definition » (l’autre définition).
Voici le programme qui la provoque :
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Counter { count: 0 } }
fn reset(&mut self) { self.count = 0; }}
impl Counter { fn reset(&mut self) { // ❌ `reset` est déjà défini plus haut self.count = 0; }}
fn main() { let mut c = Counter::new(); c.reset(); println!("{}", c.count);}rustc signale aussi E0034, « multiple applicable items in scope » (plusieurs éléments applicables dans la portée), sur l’appel c.reset(), car il ne sait pas lequel des deux reset vous voulez. C’est une erreur secondaire : corrigez E0592, et E0034 disparaît avec.
Ce que ça veut dire
Section intitulée « Ce que ça veut dire »Le titre de l’erreur donne le nom défini deux fois (reset). Le ^^^ marque une définition, et la ligne --- marque l’« other definition ». rustc affiche les numéros de ligne des deux, pour que vous puissiez y aller directement. Quand elles sont loin l’une de l’autre, les ... entre les deux veulent juste dire « quelques lignes sautées ».
Peu importe que les deux reset soient dans des blocs impl Counter différents, ou même dans des fichiers différents de votre crate. Pour Rust, ce sont toutes des méthodes du même type, donc Counter::reset doit désigner exactement une fonction. La même règle s’applique aux constantes associées : deux const MAX: u32 pour un même type, c’est aussi E0592.
C’est comme un répertoire de téléphone avec deux contacts appelés « Maman ». Quand vous dites « appelle Maman », quel numéro doit-il composer ?
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »Répartir les méthodes sur plusieurs blocs impl est permis et souvent utile : un bloc pour les constructeurs, un pour les accesseurs, un derrière un #[cfg(test)]. Voir Ajouter du comportement avec des méthodes. Mais quand vous appelez c.reset() ou Counter::reset(&mut c), Rust doit trouver une fonction. S’il y en avait deux, celle qui s’exécute dépendrait de détails que personne ne voit en lisant l’appel.
Rust refuse donc dès la définition, pas seulement à l’appel. Ça attrape un vrai bug : avec une méthode copiée-collée, vous pourriez corriger un problème dans une copie pendant que l’autre continue de tourner. Rust s’assure qu’il n’y a toujours qu’une seule version à corriger.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : supprimer le doublon et garder une seule définition
Section intitulée « Solution 1 : supprimer le doublon et garder une seule définition »Choisissez-la la plupart du temps. Si les deux copies font la même chose (copier-coller, merge raté, méthode déplacée mais pas supprimée), supprimez-en une. Avant de supprimer, comparez les deux corps : si l’un contient une correction que l’autre n’a pas, gardez celui-là. Fusionner les blocs impl en un seul rend aussi les doublons plus faciles à repérer à l’avenir.
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Counter { count: 0 } }
fn increment(&mut self) { self.count += 1; }
fn reset(&mut self) { // ✅ un seul `reset` pour `Counter` self.count = 0; }}
fn main() { let mut c = Counter::new(); c.increment(); c.increment(); println!("avant reset : {}", c.count); c.reset(); println!("après reset : {}", c.count);}Sortie :
avant reset : 2après reset : 0Si vous ne trouvez pas la deuxième définition, cherchez dans tout le projet : grep -rn "fn reset" src/. Un bloc impl Counter peut se trouver dans n’importe quel module de votre crate.
Solution 2 : renommer l’une des deux
Section intitulée « Solution 2 : renommer l’une des deux »Choisissez-la quand les deux définitions font des choses différentes. Rust n’a pas de surcharge (overloading) : vous ne pouvez pas avoir deux méthodes du même nom avec des paramètres différents, comme reset() et reset(start: u32). C’est aussi E0592. Donnez à chacune un nom qui dit ce qu’elle fait :
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Counter { count: 0 } }
fn reset(&mut self) { self.count = 0; }}
impl Counter { fn reset_to(&mut self, start: u32) { // ✅ un nom différent pour un comportement différent self.count = start; }}
fn main() { let mut c = Counter::new(); c.reset_to(10); println!("après reset_to(10) : {}", c.count); c.reset(); println!("après reset() : {}", c.count);}Sortie :
après reset_to(10) : 10après reset() : 0Les conventions de nommage courantes en Rust aident ici : new et with_capacity, from_str et from_bytes, get et get_mut. Si vous voulez vraiment un seul nom pour plusieurs types d’arguments, utilisez un paramètre générique ou un trait, comme fn reset_to(&mut self, start: impl Into<u32>). Voir Les génériques.
Solution 3 : empêcher les blocs impl génériques de se chevaucher
Section intitulée « Solution 3 : empêcher les blocs impl génériques de se chevaucher »Choisissez-la quand l’une des deux définitions est dans un bloc générique comme impl<T> Wrapper<T>. Ce bloc couvre aussi Wrapper<i32>, donc un deuxième impl Wrapper<i32> avec le même nom de méthode est un doublon :
error[E0592]: duplicate definitions with name `describe` --> src/main.rs:4:5 | 4 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `describe`...10 | fn describe(&self) -> String { | ---------------------------- other definition for `describe`Le programme qui la provoque :
struct Wrapper<T>(T);
impl<T> Wrapper<T> { fn describe(&self) -> String { String::from("quelque chose") }}
impl Wrapper<i32> { fn describe(&self) -> String { format!("le nombre {}", self.0) }}
fn main() { println!("{}", Wrapper(5).describe());}Rust ne permet pas à un impl spécifique de remplacer un impl générique (cette fonctionnalité, appelée specialization ou spécialisation, n’est pas stable). Deux bonnes façons de s’en sortir : n’écrire des blocs impl que pour des types précis, pour qu’ils ne puissent pas se chevaucher, ou mettre le comportement dans un trait et l’implémenter une fois par type :
struct Wrapper<T>(T);
// Option A : des blocs impl pour des types précis ne se chevauchent jamaisimpl Wrapper<i32> { fn describe(&self) -> String { format!("le nombre {}", self.0) }}
impl Wrapper<&str> { fn describe(&self) -> String { format!("le texte \"{}\"", self.0) }}
// Option B : un trait, implémenté une fois par typetrait Label { fn label(&self) -> String;}
impl Label for i32 { fn label(&self) -> String { format!("entier {}", self) }}
impl Label for bool { fn label(&self) -> String { format!("booléen {}", self) }}
impl<T: Label> Wrapper<T> { fn label(&self) -> String { self.0.label() // une seule méthode générique, la partie propre à chaque type vit dans le trait }}
fn main() { println!("{}", Wrapper(5).describe()); println!("{}", Wrapper("salut").describe()); println!("{}", Wrapper(7).label()); println!("{}", Wrapper(true).label());}Sortie :
le nombre 5le texte "salut"entier 7booléen trueLa version avec un trait est en général la meilleure conception sur le long terme : prendre en charge un nouveau type, c’est juste un impl Label for ... de plus. Voir Les traits.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Une fonction d’aide pour les tests dans un deuxième bloc impl. Vous ajoutez impl Counter { fn new() -> Self { ... } } dans #[cfg(test)] mod tests pour construire des valeurs de test, mais Counter::new existe déjà. Le bloc de test reste un impl Counter. Correction : appelez-la par exemple fn for_test(), ou utilisez le vrai new. Voir Les tests.
Après un merge ou une refactorisation. Deux branches ont chacune ajouté un fn len(&self), ou une méthode a été déplacée dans un nouveau fichier et l’ancienne copie est restée. rustc affiche les deux numéros de ligne ; gardez la meilleure version.
Une méthode de trait du même nom, ça passe. impl Counter { fn reset(&mut self) } avec impl Resettable for Counter { fn reset(&mut self) }, ce n’est pas E0592 : l’une est la méthode propre du type, l’autre appartient au trait, et c.reset() appelle celle du type. Deux traits avec le même nom de méthode compilent aussi, mais l’appel c.reset() donne alors E0034 ; corrigez-le avec Resettable::reset(&mut c).
Erreurs liées
Section intitulée « Erreurs liées »- E0034 : multiple applicable items in scope : l’erreur secondaire à l’endroit de l’appel, et l’erreur pour deux traits avec le même nom de méthode.
- E0428 : the name is defined multiple times : la même erreur pour les éléments d’un module, comme deux
struct Counterou deux fonctions libresfn main. - E0599 : no method named x found : le problème inverse, une méthode qui n’existe pas du tout.
- E0255 : nom déjà utilisé par un import et E0252 : nom importé deux fois : des noms en double causés par
use. - Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Un type a deux blocs impl. Peuvent-ils contenir chacun une méthode appelée reset ?
2. Vous voulez area() et area(scale: f64). Que faites-vous en Rust ?
3. Pourquoi impl<T> Wrapper<T> et impl Wrapper<i32> entrent-ils en conflit quand les deux définissent describe ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0592 — Rust error codes index (en anglais) : l’explication officielle de cette erreur.
- Method Syntax — The Rust Book (en anglais) : les méthodes, les fonctions associées et la partie « Multiple
implBlocks ». - Generic Data Types — The Rust Book (en anglais) :
impl<T> Point<T>à côté deimpl Point<f32>, et le lien entre les deux. - Implementations — The Rust Reference (en anglais) : les règles exactes des blocs
implpropres à un type et de leurs éléments associés. - E0034 — Rust error codes index (en anglais) : la page officielle de l’erreur secondaire « multiple applicable items ».