Zum Inhalt springen

Rust-Fehler E0592: duplicate definitions with name X (doppelte Methode)

E0592 bedeutet: Derselbe Typ hat zwei Methoden, assoziierte Funktionen oder Konstanten mit demselben Namen, geschrieben in impl-Blöcken. Ein Typ darf beliebig viele impl-Blöcke haben, aber alle teilen sich eine einzige Namensliste. Jeder Name darf also nur einmal vorkommen. Meistens kommt der Fehler von Kopieren und Einfügen, einem Merge oder einem zweiten impl-Block in einer anderen Datei. Die Lösung: Behalte einen, oder gib dem anderen einen neuen Namen. (Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.)

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` ist oben schon definiert
| ------------------- other definition for `reset`

Auf Deutsch: „Doppelte Definitionen mit dem Namen reset.“ Die ^^^-Zeile markiert eine Definition, die ----Zeile die „andere Definition“.

Dieses Programm erzeugt den Fehler:

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` ist oben schon definiert
self.count = 0;
}
}
fn main() {
let mut c = Counter::new();
c.reset();
println!("{}", c.count);
}

rustc meldet beim Aufruf c.reset() außerdem E0034, „multiple applicable items in scope“ (mehrere passende Elemente im Gültigkeitsbereich), weil es nicht weiß, welches der beiden reset du meinst. Das ist ein Folgefehler: Behebst du E0592, verschwindet E0034 gleich mit.

Die Überschrift nennt den Namen, der doppelt definiert ist (reset). Das ^^^ markiert die eine Definition, die ----Zeile die „other definition“ (die andere Definition). rustc zeigt die Zeilennummern von beiden, also kannst du direkt hinspringen. Liegen sie weit auseinander, bedeutet das ... dazwischen nur „einige Zeilen ausgelassen“.

Es spielt keine Rolle, dass die beiden reset in verschiedenen impl Counter-Blöcken stehen, oder sogar in verschiedenen Dateien deines Crates. Für Rust sind sie alle Methoden desselben Typs, also muss Counter::reset genau eine Funktion bedeuten. Dieselbe Regel gilt für assoziierte Konstanten: Zwei const MAX: u32 für einen Typ ergeben ebenfalls E0592.

Das ist wie eine Kontaktliste im Handy mit zwei Einträgen namens „Mama“. Wenn du sagst „Ruf Mama an“, welche Nummer soll es wählen?

Methoden auf mehrere impl-Blöcke zu verteilen, ist erlaubt und oft nützlich: ein Block für Konstruktoren, einer für Getter, einer hinter #[cfg(test)]. Siehe Verhalten mit Methoden hinzufügen. Aber wenn du c.reset() oder Counter::reset(&mut c) aufrufst, muss Rust eine Funktion finden. Gäbe es zwei, hinge es von Details ab, die niemand beim Lesen des Aufrufs sieht, welche davon läuft.

Deshalb lehnt Rust schon die Definition ab, nicht erst den Aufruf. Das fängt einen echten Bug ab: Bei einer kopierten Methode behebst du vielleicht ein Problem in der einen Kopie, während die andere weiterläuft. Rust sorgt dafür, dass es immer nur eine Version gibt, die du reparieren musst.

Lösung 1: Die Kopie löschen und eine Definition behalten

Abschnitt betitelt „Lösung 1: Die Kopie löschen und eine Definition behalten“

Nimm diese Lösung in den meisten Fällen. Tun beide Kopien dasselbe (Kopieren und Einfügen, ein unordentlicher Merge, eine Methode, die du verschoben, aber nicht entfernt hast), lösch eine davon. Vergleich vorher die beiden Rümpfe: Hat einer eine Korrektur, die dem anderen fehlt, behalte diesen. Fasst du die impl-Blöcke zu einem zusammen, fallen Duplikate in Zukunft auch leichter auf.

struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Counter { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
fn reset(&mut self) { // ✅ nur ein `reset` für `Counter`
self.count = 0;
}
}
fn main() {
let mut c = Counter::new();
c.increment();
c.increment();
println!("vor reset: {}", c.count);
c.reset();
println!("nach reset: {}", c.count);
}

Ausgabe:

vor reset: 2
nach reset: 0
▶ Selbst ausprobieren

Findest du die zweite Definition nicht, durchsuch das ganze Projekt: grep -rn "fn reset" src/. Ein impl Counter-Block kann in jedem Modul deines Crates stehen.

Nimm diese Lösung, wenn die beiden Definitionen Verschiedenes tun. Rust kennt kein Überladen: Du kannst keine zwei Methoden mit demselben Namen und verschiedenen Parametern haben, etwa reset() und reset(start: u32). Auch das ist E0592. Gib jeder einen Namen, der sagt, was sie tut:

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) { // ✅ anderer Name für anderes Verhalten
self.count = start;
}
}
fn main() {
let mut c = Counter::new();
c.reset_to(10);
println!("nach reset_to(10): {}", c.count);
c.reset();
println!("nach reset(): {}", c.count);
}

Ausgabe:

nach reset_to(10): 10
nach reset(): 0
▶ Selbst ausprobieren

Die üblichen Namensmuster in Rust helfen dabei: new und with_capacity, from_str und from_bytes, get und get_mut. Willst du wirklich einen Namen für mehrere Argumenttypen, nimm einen generischen Parameter oder einen Trait, etwa fn reset_to(&mut self, start: impl Into<u32>). Siehe Generics.

Lösung 3: Verhindern, dass sich generische impl-Blöcke überschneiden

Abschnitt betitelt „Lösung 3: Verhindern, dass sich generische impl-Blöcke überschneiden“

Nimm diese Lösung, wenn eine der beiden Definitionen in einem generischen Block wie impl<T> Wrapper<T> steht. Dieser Block deckt auch Wrapper<i32> ab, also ist ein zweites impl Wrapper<i32> mit demselben Methodennamen ein Duplikat:

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`

Das Programm dazu:

struct Wrapper<T>(T);
impl<T> Wrapper<T> {
fn describe(&self) -> String {
String::from("irgendetwas")
}
}
impl Wrapper<i32> {
fn describe(&self) -> String {
format!("die Zahl {}", self.0)
}
}
fn main() {
println!("{}", Wrapper(5).describe());
}

Rust erlaubt nicht, dass ein spezielles impl ein generisches überschreibt (dieses Feature heißt Specialization und ist nicht stabil). Es gibt zwei gute Auswege: Schreib impl-Blöcke nur für konkrete Typen, damit sie sich nicht überschneiden können, oder pack das Verhalten in einen Trait und implementiere ihn einmal pro Typ:

struct Wrapper<T>(T);
// Variante A: impl-Blöcke für konkrete Typen überschneiden sich nie
impl Wrapper<i32> {
fn describe(&self) -> String {
format!("die Zahl {}", self.0)
}
}
impl Wrapper<&str> {
fn describe(&self) -> String {
format!("der Text \"{}\"", self.0)
}
}
// Variante B: ein Trait, einmal pro Typ implementiert
trait Label {
fn label(&self) -> String;
}
impl Label for i32 {
fn label(&self) -> String {
format!("Ganzzahl {}", self)
}
}
impl Label for bool {
fn label(&self) -> String {
format!("Flag {}", self)
}
}
impl<T: Label> Wrapper<T> {
fn label(&self) -> String {
self.0.label() // eine generische Methode, der Teil pro Typ steckt im Trait
}
}
fn main() {
println!("{}", Wrapper(5).describe());
println!("{}", Wrapper("hi").describe());
println!("{}", Wrapper(7).label());
println!("{}", Wrapper(true).label());
}

Ausgabe:

die Zahl 5
der Text "hi"
Ganzzahl 7
Flag true
▶ Selbst ausprobieren

Die Trait-Version ist auf lange Sicht meistens das bessere Design: Einen neuen Typ zu unterstützen, ist nur ein weiteres impl Label for .... Siehe Traits.

Eine Test-Hilfsfunktion in einem zweiten impl-Block. Du schreibst impl Counter { fn new() -> Self { ... } } in #[cfg(test)] mod tests, um Testwerte zu bauen, aber Counter::new gibt es schon. Auch der Test-Block ist ein impl Counter. Lösung: Nenn sie etwa fn for_test(), oder benutze das echte new. Siehe Tests.

Nach einem Merge oder Refactoring. Zwei Branches haben beide ein fn len(&self) hinzugefügt, oder eine Methode wurde in eine neue Datei verschoben, und die alte Kopie ist geblieben. rustc zeigt beide Zeilennummern; behalte die bessere.

Eine Trait-Methode mit demselben Namen ist in Ordnung. impl Counter { fn reset(&mut self) } zusammen mit impl Resettable for Counter { fn reset(&mut self) } ist kein E0592: Die eine ist die eigene Methode des Typs, die andere gehört zum Trait, und c.reset() ruft die eigene Methode des Typs auf. Auch zwei Traits mit demselben Methodennamen kompilieren, aber der Aufruf c.reset() ergibt dann E0034. Das löst du mit Resettable::reset(&mut c).

Kurzer Check

1. Ein Typ hat zwei impl-Blöcke. Dürfen beide eine Methode namens reset enthalten?

2. Du willst area() und area(scale: f64). Was machst du in Rust?

3. Warum kollidieren impl<T> Wrapper<T> und impl Wrapper<i32>, wenn beide describe definieren?

Punkte: 0 / 3