Zum Inhalt springen

Rust-Fehler E0252: the name is defined multiple times (zwei Imports)

E0252 bedeutet: Zwei use-Zeilen holen denselben Namen herein. Das Lehrbuchbeispiel ist use std::fmt::Result; zusammen mit use std::io::Result;: zwei verschiedene Typen, die beide Result heißen. Andere Paare sind fmt::Error und io::Error oder die beiden Write-Traits. Der Fehler ist sehr häufig und leicht zu beheben, meistens änderst du nur eine oder zwei use-Zeilen. Auch rustc 1.97 meldet ihn noch unter seinem eigenen Code E0252. Die Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.

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` here
2 | use std::io::Result; // ❌ ein zweites `Result`
| ^^^^^^^^^^^^^^^ `Result` reimported here
|
= note: `Result` must be defined only once in the type namespace of this module
help: you can use `as` to change the binding name of the import
|
2 | use std::io::Result as OtherResult; // ❌ ein zweites `Result`
| ++++++++++++++

Auf Deutsch: „Der Name Result ist mehrfach definiert.“ Die note sagt: „Result darf im Typ-Namensraum dieses Moduls nur einmal definiert sein.“

Dieses Programm erzeugt den Fehler:

use std::fmt::Result;
use std::io::Result; // ❌ ein zweites `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("keine Zahl")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("Fehler: {}", e),
}
}

Nach diesem Fehler gibt rustc noch vier weitere aus: E0107 („type alias takes 0 generic arguments“, der Typalias nimmt 0 generische Argumente), zweimal E0308 (mismatched types, unpassende Typen) und ein E0277. Lauf ihnen nicht hinterher. Rust hat den ersten Import behalten, also bedeutet jedes Result in der Datei jetzt fmt::Result, und parse_temperature ergibt keinen Sinn mehr. Behebst du E0252, verschwinden sie alle.

rustc zeigt auf zwei use-Zeilen:

  1. Die ----Zeile (Zeile 1): „previous import of the type Result here“ (hier wurde der Typ Result schon importiert). Diese Zeile kam zuerst, also behält Rust sie.
  2. Die ^^^-Zeile (Zeile 2): „Result reimported here“ (Result hier erneut importiert). Der zweite Import will denselben Namen haben.
  3. Das help: schlägt vor, den zweiten Import mit as umzubenennen.

std::fmt::Result und std::io::Result sind wirklich verschieden. fmt::Result ist ein einfaches Result<(), fmt::Error>, das Display und Debug benutzen. io::Result<T> ist Result<T, io::Error>, das du bei Dateien und im Netzwerk bekommst. Gleicher Name, andere Aufgabe.

Das ist so, als würdest du zwei Kontakte in deinem Handy als „Mama“ speichern: deine Mutter und die Mutter deines Partners. Tippst du auf „Mama“, muss das Handy wissen, wen es anrufen soll. Also ergänzt du bei einem Kontakt ein Wort, oder du speicherst sie als „Mama (Ana)“ und „Mama (Lee)“.

Innerhalb eines Moduls zeigt jeder Name auf genau ein Element. Eine use-Zeile kopiert nichts. Sie fügt nur einen kurzen Namen für ein Element hinzu, das woanders liegt. Zwei use-Zeilen mit demselben kurzen Namen würden jedes spätere Result mehrdeutig machen. Deshalb hält Rust schon beim Import an, statt bei jeder Verwendung zu raten.

E0252 ist einer von drei Geschwistern mit derselben Überschrift: E0255 ist eine use-Zeile gegen dein eigenes Element, und E0428 sind zwei deiner eigenen Elemente. Hinter allen dreien steckt die Regel „ein Name, ein Ding, pro Modul“. Siehe Module.

Lösung 1: Die Module importieren und fmt:: und io:: schreiben

Abschnitt betitelt „Lösung 1: Die Module importieren und fmt:: und io:: schreiben“

Nimm diese Lösung in den meisten Fällen, und immer bei Result und Error. Importiere die übergeordneten Module und schreib den Modulnamen davor. Jetzt sagt jede Verwendung, welches Result gemeint ist. Das Rust-Buch empfiehlt genau für diesen Fall diesen Stil:

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("keine Zahl")),
}
}
fn main() {
for input in ["21", " -4 ", "warm"] {
match parse_temperature(input) {
Ok(t) => println!("{}", t),
Err(e) => println!("Fehler: {}", e),
}
}
}

Ausgabe:

21 °C
-4 °C
Fehler: keine Zahl
▶ Selbst ausprobieren

Bonus: Das normale Result<T, E> aus dem Prelude kannst du weiterhin ohne Import benutzen.

Nimm diese Lösung, wenn du einen der beiden Typen oft benutzt und einen kurzen Namen dafür willst. Das ist der Vorschlag von rustc. Wähle einen Namen, der sagt, woher der Typ kommt, etwa IoResult statt 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("keine Zahl")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("Fehler: {}", e),
}
}

Ausgabe:

21 °C
▶ Selbst ausprobieren

Ein einfaches Result, das heimlich fmt::Result bedeutet, kann die nächste Person überraschen, die den Code liest. Lösung 1 ist deshalb meistens klarer. Seine Stärke zeigt as bei langen Namen, etwa use std::collections::BTreeMap as Map;.

Nimm diese Lösung bei Traits, die du nur importierst, um ihre Methoden aufzurufen. Die beiden Write-Traits sind der Klassiker: Mit fmt::Write kannst du per write! in einen String schreiben, mit io::Write per write! auf stdout oder in eine Datei. Importierst du beide mit Namen, kollidieren sie:

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` here
2 | use std::io::Write; // ❌ ein zweites `Write`
| ^^^^^^^^^^^^^^ `Write` reimported here
|
= note: `Write` must be defined only once in the type namespace of this module
help: you can use `as` to change the binding name of the import
|
2 | use std::io::Write as OtherWrite; // ❌ ein zweites `Write`
| +++++++++++++

Das fehlerhafte Programm:

use std::fmt::Write;
use std::io::Write; // ❌ ein zweites `Write`
fn main() {
let mut report = String::new();
write!(report, "{} Tests bestanden", 12).unwrap();
let mut out = std::io::stdout();
writeln!(out, "{}", report).unwrap();
}

Du schreibst Write hier nie selbst hin, du brauchst nur seine Methoden. as _ importiert einen Trait ohne ihm einen Namen zu geben. So kann nichts kollidieren, und beide Sätze von Methoden funktionieren trotzdem:

use std::fmt::Write as _;
use std::io::Write as _;
fn main() {
let mut report = String::new();
write!(report, "{} Tests bestanden", 12).unwrap();
let mut out = std::io::stdout();
writeln!(out, "{}", report).unwrap();
}

Ausgabe:

12 Tests bestanden
▶ Selbst ausprobieren

Musst du einen der beiden doch beim Namen nennen, zum Beispiel in fn log(w: &mut impl io::Write), kombinier das mit Lösung 1: use std::io; und dann io::Write. Siehe Traits.

Lösung 4: Einen doppelten Import desselben Elements entfernen

Abschnitt betitelt „Lösung 4: Einen doppelten Import desselben Elements entfernen“

Nimm diese Lösung, wenn beide Zeilen genau dasselbe importieren. Das passiert oft nach dem Zusammenführen von Code, oder wenn dein Editor einen Import ergänzt, den du schon hattest:

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` here
2 | use std::collections::{HashMap, HashSet}; // ❌ schon wieder `HashMap`
| ^^^^^^^--
| |
| `HashMap` reimported here
| help: remove unnecessary import

Das fehlerhafte Programm:

use std::collections::HashMap;
use std::collections::{HashMap, HashSet}; // ❌ schon wieder `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!("Äpfel: {}, ausverkauft: {}", stock["apples"], sold_out.len());
}

Beachte, dass die Hilfe hier anders lautet: kein as, nur „remove unnecessary import“ (unnötigen Import entfernen). Behalte eine Zeile:

use std::collections::{HashMap, HashSet};
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!("Äpfel: {}, ausverkauft: {}", stock["apples"], sold_out.len());
}

Ausgabe:

Äpfel: 3, ausverkauft: 1
▶ Selbst ausprobieren

Siehe HashMap in Rust und HashSet und BTreeMap.

Error aus zwei Quellen. use std::fmt::Error; und use std::io::Error;, oder use std::error::Error; neben use anyhow::Error;. Lösung: use std::io; und dann io::Error, oder use anyhow::Error as AnyError;. Siehe anyhow und thiserror.

Zwei Crates mit demselben Typnamen. use chrono::Duration; und use std::time::Duration; sind ein häufiges Paar in Projekten, die mit Datumsangaben arbeiten. Lösung: Behalte für einen den kurzen Namen und schreib beim anderen das Modul davor, etwa chrono::Duration. Siehe Datum und Uhrzeit.

Glob-Imports. use std::fmt::*; plus use std::io::*; ergibt an den use-Zeilen kein E0252. Der Ärger kommt später, beim ersten Result, als Fehler ohne Code: error: `Result` is ambiguous (Result ist mehrdeutig). Lösung: Importiere die Namen, die du brauchst, ausdrücklich, wie in Lösung 1.

  • E0255: Eine use-Zeile kollidiert mit deinem eigenen Struct, Enum oder deiner eigenen Funktion mit demselben Namen.
  • E0428: Zwei deiner eigenen Elemente haben denselben Namen, oft weil du eine Funktion überladen wolltest.
  • E0432: unresolved import – die use-Zeile zeigt auf etwas, das es nicht gibt.
  • E0034: Zwei Traits im Gültigkeitsbereich geben einem Typ Methoden mit demselben Namen.
  • E0308: mismatched types, einer der Folgefehler nach einer Result-Kollision.
  • Alle Compilerfehler: die vollständige Liste der Fehlerseiten.

Kurzer Check

1. Du brauchst fmt::Result und io::Result in derselben Datei. Welche Imports sind die übliche saubere Wahl?

2. Du brauchst die Methoden von std::fmt::Write und std::io::Write, schreibst die Trait-Namen aber nie selbst. Was funktioniert?

3. rustc sagt, HashMap sei hier erneut importiert, und schlägt remove unnecessary import vor. Was ist passiert?

Punkte: 0 / 3