Rust-Fehler E0428: the name is defined multiple times (doppelter Name)
E0428 bedeutet: Du hast zwei Dinge mit demselben Namen an derselben Stelle definiert – zwei Funktionen namens area, zwei Structs namens User oder ein Enum und ein Struct, die beide Status heißen. Oft passiert das, wenn du eine Funktion überladen willst, so wie in Java, C# oder C++. Rust kennt kein Überladen, hat aber gute Alternativen, und diese Seite zeigt sie dir. Sie gehört zur Reihe Rust-Compilerfehler einfach erklärt.
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0428]: the name `area` is defined multiple times --> src/main.rs:5:1 |1 | fn area(width: f64, height: f64) -> f64 { | --------------------------------------- previous definition of the value `area` here...5 | fn area(radius: f64) -> f64 { // ❌ ein zweites `area` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `area` redefined here | = note: `area` must be defined only once in the value namespace of this moduleAuf Deutsch: „Der Name area ist mehrfach definiert.“ Die note sagt: „area darf im Werte-Namensraum dieses Moduls nur einmal definiert sein.“
Dieses Programm erzeugt den Fehler:
fn area(width: f64, height: f64) -> f64 { width * height}
fn area(radius: f64) -> f64 { // ❌ ein zweites `area` 3.14159 * radius * radius}
fn main() { println!("{}", area(2.0, 3.0)); println!("{}", area(1.0));}Für dieses Programm gibt rustc noch einen zweiten Fehler aus: E0061, „this function takes 2 arguments but 1 argument was supplied“ (diese Funktion nimmt 2 Argumente, aber nur 1 wurde übergeben), bei area(1.0). Das ist nur eine Nebenwirkung. Rust hat das erste area behalten und das zweite ignoriert, also passt der Aufruf mit einem Argument nicht mehr. Behebst du E0428, verschwindet E0061 gleich mit.
Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“rustc zeigt auf zwei Stellen:
- Die
----Zeile (Zeile 1): „previous definition of the valueareahere“ (hier ist die frühere Definition vonarea). Das ist das erstearea, das Rust behält. - Die
^^^-Zeile (Zeile 5): „arearedefined here“ (areahier erneut definiert). Das ist das zweite, und dieses wird abgelehnt. - Die
note:sagt, dassarea„in the value namespace of this module“ nur einmal definiert sein darf, also im Werte-Namensraum dieses Moduls. Jedes Modul ist wie eine Schublade, und in einer Schublade muss jeder Name eindeutig sein.
Bei zwei Structs steht in der Notiz stattdessen „type namespace“ (Typ-Namensraum). Rust führt Typen (Structs, Enums, Traits, Module) und Werte (Funktionen, Konstanten, Statics) in zwei getrennten Listen. Deshalb können ein mod shapes und ein fn shapes nebeneinander existieren, zwei fn area oder zwei struct User aber nicht.
Denk an zwei Dateien namens report.txt im selben Ordner. Dein Computer lehnt die zweite ab – nicht, weil mit ihr etwas nicht stimmt, sondern weil report.txt dann nicht mehr sagen würde, welche Datei du meinst.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“In Rust zeigt ein Name in einem Modul auf genau ein Element. Schreibst du area(...), findet Rust die Funktion allein über ihren Namen, nicht über die Anzahl oder die Typen der Argumente. Das macht Code leicht lesbar: Es gibt nur ein area, das du nachschlagen musst, und eine neue Funktion kann nie stillschweigend ändern, welche Funktion ein alter Aufruf benutzt.
Sprachen mit Überladen wählen die Version anhand der Argumente aus, und die Regeln werden knifflig, sobald Zahlen ineinander umgewandelt werden oder Standardwerte dazukommen. Rust hat sich dagegen entschieden. Willst du „denselben Namen für verschiedene Eingaben“, benutzt du Traits oder Generics. Dort siehst du die Wahl an den Typen. Mehr über Funktionen findest du unter Funktionen.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Jeder Funktion einen eigenen Namen geben
Abschnitt betitelt „Lösung 1: Jeder Funktion einen eigenen Namen geben“Nimm diese Lösung in den meisten Fällen. Zwei Funktionen mit verschiedenen Eingaben erledigen meistens verschiedene Aufgaben, und der Name kann sagen, welche. War eine davon eine alte Kopie, die du vergessen hast zu löschen, lösch sie stattdessen:
fn rectangle_area(width: f64, height: f64) -> f64 { width * height}
fn circle_area(radius: f64) -> f64 { 3.14159 * radius * radius}
fn main() { println!("{}", rectangle_area(2.0, 3.0)); println!("{}", circle_area(1.0));}Ausgabe:
63.14159Die Standardbibliothek macht es genauso: String::new(), String::with_capacity(10) und String::from("hi") sind drei verschiedene Namen für drei Arten, einen String zu erzeugen.
Lösung 2: Sie in getrennte Module packen
Abschnitt betitelt „Lösung 2: Sie in getrennte Module packen“Nimm diese Lösung, wenn der kurze Name in jedem Zusammenhang wirklich der beste ist, etwa area für jede Form, oder ein User in deinem Datenbank-Code und ein anderer User in deinem API-Code. Jedes Modul hat seine eigenen Namen, also verschwindet die Kollision. Beim Aufruf schreibst du den Modulnamen davor:
mod rectangle { pub fn area(width: f64, height: f64) -> f64 { width * height }}
mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius }}
fn main() { println!("{}", rectangle::area(2.0, 3.0)); println!("{}", circle::area(1.0));}Ausgabe:
63.14159Vergiss das pub nicht, sonst ergibt der Aufruf aus main den Fehler E0603, „function is private“ (die Funktion ist privat). Siehe Module.
Lösung 3: Einen Trait statt Überladen benutzen
Abschnitt betitelt „Lösung 3: Einen Trait statt Überladen benutzen“Nimm diese Lösung, wenn du einen Aufruf für viele Typen willst – meistens ist genau das der Zweck von Überladen. Leg für jede Art von Eingabe einen Typ an und gib ihnen einen gemeinsamen Trait. Jetzt hat jede Form ihr eigenes area, und Rust wählt das richtige anhand des Typs des Werts:
trait Shape { fn area(&self) -> f64;}
struct Rectangle { width: f64, height: f64,}
struct Circle { radius: f64,}
impl Shape for Rectangle { fn area(&self) -> f64 { self.width * self.height }}
impl Shape for Circle { fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }}
fn print_area<T: Shape>(shape: &T) { println!("Fläche: {}", shape.area());}
fn main() { print_area(&Rectangle { width: 2.0, height: 3.0 }); print_area(&Circle { radius: 1.0 });}Ausgabe:
Fläche: 6Fläche: 3.14159Das ist mehr Code als Lösung 1. Es lohnt sich also, wenn du mehrere Formen oder mehrere Funktionen hast (area, perimeter, draw), die alle demselben Muster folgen. Das generische print_area<T: Shape> funktioniert für jede Form, die du später hinzufügst. Siehe Traits und Generics.
Für „dieselbe Funktion mit oder ohne zusätzliches Argument“ gibt es eine leichtere Möglichkeit: Nimm eine Option, etwa fn greet(name: &str, title: Option<&str>), und ruf sie mit None oder Some("Dr.") auf.
Lösung 4: Einen Import mit use ... as ... umbenennen
Abschnitt betitelt „Lösung 4: Einen Import mit use ... as ... umbenennen“Nimm diese Lösung, wenn die Kollision zwischen deinem eigenen Element und einer use-Zeile besteht. Genau genommen ist das ein verwandter Fehler, E0255, mit derselben Überschrift:
error[E0255]: the name `Error` is defined multiple times --> src/main.rs:3:1 |1 | use std::io::Error; | -------------- previous import of the type `Error` here2 |3 | enum Error { | ^^^^^^^^^^ `Error` redefined here | = note: `Error` must be defined only once in the type namespace of this modulehelp: you can use `as` to change the binding name of the import |1 | use std::io::Error as OtherError; | +++++++++++++Das help sagt: „Mit as kannst du dem Import einen anderen Namen geben.“ Gib dem Import mit as einen lokalen Spitznamen. Wähle einen Namen, der sagt, woher er kommt, etwa IoError, statt des Platzhalters OtherError von rustc:
use std::io::Error as IoError;use std::io::ErrorKind;
#[derive(Debug)]enum Error { NotFound(String), Invalid(u32),}
fn main() { let mine = Error::NotFound(String::from("config.toml")); let other = Error::Invalid(42); let disk = IoError::new(ErrorKind::Other, "Festplatte ist voll");
println!("{:?}", mine); println!("{:?}", other); println!("{}", disk);}Ausgabe:
NotFound("config.toml")Invalid(42)Festplatte ist vollZwei use-Zeilen, die kollidieren, etwa use std::fmt::Result; und use std::io::Result;, ergeben einen dritten Verwandten, E0252 (`Result` reimported here, Result hier erneut importiert). Die Lösung ist dieselbe: use std::io::Result as IoResult;, oder du importierst das Modul (use std::io;) und schreibst io::Result. Dein eigenes Enum umzubenennen (AppError), funktioniert auch.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Eine Methode in einem impl-Block überladen. Zwei fn new in impl Timer ergeben einen anderen Code, E0592: duplicate definitions with name `new` (doppelte Definitionen mit dem Namen new). Gleiche Ursache, gleiche Lösung: Benenn sie nach dem, was sie tun, etwa Timer::new() und Timer::with_seconds(30). Siehe Structs.
Kopieren und Einfügen beim Refactoring. Du kopierst eine Funktion, um sie zu ändern, oder fügst ein struct User aus einer anderen Datei ein, und vergisst, das alte zu entfernen. Auch in Tests passiert das oft: Zwei #[test] fn it_works() in einem mod tests ergeben E0428. Lösung: Lösch die alte Kopie, oder benenn den Test um.
Doppelte Enum-Varianten. Auch enum Color { Red, Red } ergibt E0428. Der Fehler zeigt auf das zweite Red, mit der Notiz „type namespace of this enum“ (Typ-Namensraum dieses Enums). Lösung: Entferne die doppelte Variante oder benenn sie um. Siehe Enums.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0061: falsche Anzahl von Argumenten. Kommt oft direkt nach E0428, wenn du eine Funktion überladen wolltest.
- E0425: das umgekehrte Problem – ein Name, der dort, wo du ihn benutzt, gar nicht definiert ist.
- E0433: ein Pfad wie
circle::area, dessen erster Teil (das Modul) nicht gefunden wird. - E0034: Zwei Traits geben einem Typ Methoden mit demselben Namen. Du musst sagen, welche du meinst.
- E0255 (ein
usekollidiert mit einem lokalen Element), E0252 (zweiuse-Zeilen kollidieren), E0592 (doppelte Methode in einemimpl): die Verwandten von oben. - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Du schreibst fn total(a: u32) und fn total(a: u32, b: u32) in eine Datei. Was passiert?
2. Können mod report und fn report() im selben Modul existieren?
3. Deine Datei hat use std::fmt::Result;, und du definierst enum Result. Was ist eine saubere Lösung?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0428 – Rust error codes index (auf Englisch): die offizielle Erklärung, mit dem Beispiel mit zwei Structs.
- Gültigkeitsbereich und Sichtbarkeit mit Modulen steuern – Das Rust-Buch: wie Module jedem Teil deines Codes eigene Namen geben.
- Mit dem Schlüsselwort as neue Namen vergeben – Das Rust-Buch: das Beispiel mit
fmt::Resultundio::Result, gelöst mitas. - Gemeinsames Verhalten definieren mit Traits – Das Rust-Buch: Verhalten zwischen Typen teilen, die Antwort von Rust auf Überladen.
- Namespaces – The Rust Reference (auf Englisch): die genauen Regeln für den Typ- und den Werte-Namensraum, die in der Notiz des Fehlers vorkommen.