Salta ai contenuti

Errore Rust E0252: the name is defined multiple times (due import)

E0252 significa che due righe use portano lo stesso nome. Il caso da manuale è use std::fmt::Result; più use std::io::Result;: due tipi diversi, entrambi chiamati Result. Altri casi sono fmt::Error e io::Error, oppure i due trait Write. È un errore molto comune e facile da correggere, di solito cambiando una o due righe use. rustc 1.97 lo segnala ancora con il suo codice, E0252. Fa parte della guida agli errori del compilatore.

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; // ❌ a second `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; // ❌ a second `Result`
| ++++++++++++++

Il compilatore dice: il nome Result è definito più volte, perché la seconda riga use lo importa di nuovo. Questo è il programma che produce l’errore:

use std::fmt::Result;
use std::io::Result; // ❌ a second `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),
}
}

Dopo questo, rustc stampa altri quattro errori: E0107 («type alias takes 0 generic arguments»), due E0308 (tipi che non corrispondono) e un E0277. Non inseguirli. Rust ha tenuto il primo import, quindi ogni Result nel file ora significa fmt::Result, e parse_temperature non ha più senso. Correggi E0252 e spariscono tutti.

rustc ti indica due righe use:

  1. La riga con --- (riga 1): «previous import of the type Result here» (import precedente del tipo Result). È arrivata per prima, quindi è quella che Rust tiene.
  2. La riga con ^^^ (riga 2): «Result reimported here» (Result importato di nuovo qui). Il secondo import cerca di prendere lo stesso nome.
  3. L’help: suggerisce di rinominare il secondo import con as.

std::fmt::Result e std::io::Result sono davvero diversi. fmt::Result è un semplice Result<(), fmt::Error>, usato da Display e Debug. io::Result<T> è Result<T, io::Error>, usato da file e rete. Stesso nome, lavori diversi.

È come salvare nel telefono due contatti chiamati «Mamma»: tua madre e la madre del tuo partner. Quando tocchi «Mamma», il telefono deve sapere chi chiamare. Aggiungi una parola a uno dei due, oppure li salvi come «Mamma (Anna)» e «Mamma (Lucia)».

Dentro un modulo, ogni nome indica esattamente un item. Una riga use non copia niente; aggiunge un nome corto per un item che vive altrove. Due righe use che aggiungono lo stesso nome corto renderebbero ambiguo ogni Result successivo, quindi Rust si ferma all’import invece di tirare a indovinare a ogni uso.

E0252 è uno di tre fratelli con la stessa prima riga: E0255 è una riga use contro un tuo item, ed E0428 sono due tuoi item. La regola dietro a tutti e tre è «un nome, una cosa, per modulo». Vedi Moduli in Rust.

Soluzione 1: importa i moduli e scrivi fmt:: e io::

Sezione intitolata “Soluzione 1: importa i moduli e scrivi fmt:: e io::”

Scegli questa nella maggior parte dei casi, e sempre per Result ed Error. Importa i moduli genitori, poi scrivi il nome del modulo davanti. Ogni uso ora dice di quale Result si tratta. Il Rust Book consiglia proprio questo stile per questo caso:

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("non è un numero")),
}
}
fn main() {
for input in ["21", " -4 ", "caldo"] {
match parse_temperature(input) {
Ok(t) => println!("{}", t),
Err(e) => println!("errore: {}", e),
}
}
}

Output:

21 °C
-4 °C
errore: non è un numero
▶ Provalo tu

In più: il semplice Result<T, E> del prelude resta libero da usare, senza nessun import.

Scegli questa quando usi molto uno dei due tipi e vuoi un nome corto per lui. È il suggerimento di rustc. Scegli un nome che dica da dove viene, come IoResult, piuttosto che 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("non è un numero")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("errore: {}", e),
}
}

Output:

21 °C
▶ Provalo tu

Un Result semplice che in segreto significa fmt::Result può sorprendere chi legge dopo di te, quindi di solito la soluzione 1 è più chiara. as dà il meglio con i nomi lunghi, come use std::collections::BTreeMap as Map;.

Scegli questa per i trait che importi solo per chiamarne i metodi. I due trait Write sono il classico: fmt::Write ti fa usare write! dentro una String, io::Write ti fa usare write! verso lo stdout o un file. Importarli entrambi per nome crea un conflitto:

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; // ❌ a second `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; // ❌ a second `Write`
| +++++++++++++

Il programma che non compila:

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

Qui non scrivi mai Write tu stesso; ti servono solo i suoi metodi. as _ importa un trait senza dargli un nome, così niente può andare in conflitto, e entrambi i gruppi di metodi funzionano lo stesso:

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

Output:

12 test superati
▶ Provalo tu

Se ti serve nominarne uno, per esempio in fn log(w: &mut impl io::Write), combina questa con la soluzione 1: use std::io; e scrivi io::Write. Vedi Trait in Rust.

Soluzione 4: togli un import doppio dello stesso item

Sezione intitolata “Soluzione 4: togli un import doppio dello stesso item”

Scegli questa quando entrambe le righe importano proprio la stessa cosa, cosa che succede spesso dopo un merge o quando il tuo editor aggiunge un import che avevi già:

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}; // ❌ `HashMap` again
| ^^^^^^^--
| |
| `HashMap` reimported here
| help: remove unnecessary import

Il programma che non compila:

use std::collections::HashMap;
use std::collections::{HashMap, HashSet}; // ❌ `HashMap` again
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());
}

Nota che l’help è diverso: niente as, solo «remove unnecessary import» (togli l’import superfluo). Tieni una riga sola:

use std::collections::{HashMap, HashSet};
fn main() {
let mut stock: HashMap<&str, u32> = HashMap::new();
stock.insert("mele", 3);
let mut sold_out: HashSet<&str> = HashSet::new();
sold_out.insert("pere");
println!("mele: {}, esauriti: {}", stock["mele"], sold_out.len());
}

Output:

mele: 3, esauriti: 1
▶ Provalo tu

Vedi HashMap in Rust e HashSet in Rust.

Error da due posti. use std::fmt::Error; e use std::io::Error;, oppure use std::error::Error; accanto a use anyhow::Error;. Correzione: use std::io; e scrivi io::Error, oppure use anyhow::Error as AnyError;. Vedi anyhow e thiserror.

Due crate con lo stesso nome di tipo. use chrono::Duration; e use std::time::Duration; sono una coppia frequente nei progetti che gestiscono date. Correzione: tieni un nome corto e scrivi il modulo dell’altro, come chrono::Duration. Vedi Data e ora in Rust.

Import glob. use std::fmt::*; più use std::io::*; non danno E0252 sulle righe use. Il problema arriva più tardi, al primo Result, come errore senza codice: error: `Result` is ambiguous (Result è ambiguo). Correzione: importa in modo esplicito i nomi che ti servono, come nella soluzione 1.

  • E0255: una riga use va in conflitto con una tua struct, enum o funzione con lo stesso nome.
  • E0428: due tuoi item hanno lo stesso nome, spesso perché cerchi di fare l’overloading di una funzione.
  • E0432: import non risolto, quando la riga use indica qualcosa che non esiste.
  • E0034: due trait in scope danno a un tipo metodi con lo stesso nome.
  • E0308: tipi che non corrispondono, uno degli errori di conseguenza dopo un conflitto su Result.
  • Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.

Verifica veloce

1. Ti servono fmt::Result e io::Result nello stesso file. Quali import sono la scelta pulita di solito?

2. Ti servono i metodi sia di std::fmt::Write sia di std::io::Write, ma non scrivi mai i nomi dei trait. Che cosa funziona?

3. rustc dice che HashMap è stato importato di nuovo e suggerisce remove unnecessary import. Che cosa è successo?

Punteggio: 0 / 3