Ir al contenido

Error E0252 de Rust: the name is defined multiple times (dos imports)

E0252 significa que dos líneas use traen el mismo nombre. El caso de manual es use std::fmt::Result; junto con use std::io::Result;: dos tipos distintos que se llaman los dos Result. Otros ejemplos son fmt::Error e io::Error, o los dos traits Write. Es un error muy común y fácil de arreglar, normalmente cambiando una o dos líneas use. rustc 1.97 lo sigue mostrando con su propio código, E0252. Forma parte de la guía de errores del compilador.

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`
| ++++++++++++++

“The name Result is defined multiple times” quiere decir “el nombre Result está definido varias veces”. Abajo te explicamos cada parte del mensaje.

Este es el programa que lo produce:

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("no es un número")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("error: {}", e),
}
}

rustc muestra cuatro errores más después de este: E0107 (“type alias takes 0 generic arguments”, el alias de tipo no recibe argumentos genéricos), dos E0308 de tipos que no coinciden y un E0277. No vayas detrás de ellos. Rust se quedó con el primer import, así que cada Result del archivo ahora significa fmt::Result, y parse_temperature ya no tiene sentido. Arregla E0252 y desaparecen todos.

rustc señala dos líneas use:

  1. La línea --- (línea 1): “previous import of the type Result here” (aquí está el import anterior del tipo Result). Esta llegó primero, así que es la que Rust conserva.
  2. La línea ^^^ (línea 2): “Result reimported here” (aquí se vuelve a importar Result). El segundo import intenta quedarse con el mismo nombre.
  3. El help: te sugiere renombrar el segundo import con as.

std::fmt::Result y std::io::Result son de verdad distintos. fmt::Result es un simple Result<(), fmt::Error>, que usan Display y Debug. io::Result<T> es Result<T, io::Error>, que usan los archivos y la red. Mismo nombre, trabajos distintos.

Es como guardar dos contactos como “Mamá” en tu teléfono: tu madre y la madre de tu pareja. Cuando tocas “Mamá”, el teléfono tiene que saber a quién llamar. Le añades una palabra a uno de los dos, o los guardas como “Mamá (Ana)” y “Mamá (Lucía)”.

Dentro de un módulo, cada nombre apunta a exactamente un elemento. Una línea use no copia nada; añade un nombre corto para un elemento que vive en otro sitio. Dos líneas use que añaden el mismo nombre corto harían ambiguo cada Result posterior, así que Rust se detiene en el import en lugar de adivinar en cada uso.

E0252 es uno de tres hermanos con el mismo titular: E0255 es una línea use contra un elemento tuyo, y E0428 son dos elementos tuyos. La regla detrás de los tres es “un nombre, una cosa, en cada módulo”. Mira use: acortar rutas largas.

Solución 1: Importa los módulos y escribe fmt:: e io::

Sección titulada «Solución 1: Importa los módulos y escribe fmt:: e io::»

Elige esta la mayoría de las veces, y siempre con Result y Error. Importa los módulos padre y escribe el nombre del módulo delante. Ahora cada uso dice de qué Result se trata. El libro de Rust recomienda este estilo justo para este 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("no es un número")),
}
}
fn main() {
for input in ["21", " -4 ", "templado"] {
match parse_temperature(input) {
Ok(t) => println!("{}", t),
Err(e) => println!("error: {}", e),
}
}
}

Salida:

21 °C
-4 °C
error: no es un número
▶ Pruébalo tú mismo

Un extra: el Result<T, E> normal del prelude sigue libre para usarlo, sin ningún import.

Solución 2: Renombra uno de los imports con as

Sección titulada «Solución 2: Renombra uno de los imports con as»

Elige esta cuando usas mucho uno de los dos tipos y quieres un nombre corto para él. Es la sugerencia de rustc. Elige un nombre que diga de dónde viene, como IoResult, en lugar de 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("no es un número")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("error: {}", e),
}
}

Salida:

21 °C
▶ Pruébalo tú mismo

Un Result a secas que en secreto significa fmt::Result puede sorprender a quien lea el código después, así que la Solución 1 suele ser más clara. as brilla con nombres largos, como use std::collections::BTreeMap as Map;.

Elige esta con traits que solo importas para llamar a sus métodos. Los dos traits Write son el caso clásico: fmt::Write te deja usar write! sobre un String, e io::Write te deja usar write! sobre stdout o un archivo. Importar los dos por su nombre provoca el choque:

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`
| +++++++++++++

Es el mismo mensaje, ahora con el trait Write. El programa con el error:

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

Aquí nunca escribes Write tú mismo; solo necesitas sus métodos. as _ importa un trait sin darle nombre, así que nada puede chocar, y los dos grupos de métodos siguen funcionando:

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

Salida:

12 tests superados
▶ Pruébalo tú mismo

Si necesitas nombrar uno de ellos, por ejemplo en fn log(w: &mut impl io::Write), combina esto con la Solución 1: use std::io; y escribe io::Write. Mira Traits.

Solución 4: Quita un import repetido del mismo elemento

Sección titulada «Solución 4: Quita un import repetido del mismo elemento»

Elige esta cuando las dos líneas importan exactamente lo mismo, algo que pasa a menudo después de fusionar código o cuando tu editor añade un import que ya tenías:

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

El programa con el error:

use std::collections::HashMap;
use std::collections::{HashMap, HashSet}; // ❌ `HashMap` again
fn main() {
let mut stock: HashMap<&str, u32> = HashMap::new();
stock.insert("manzanas", 3);
let mut sold_out: HashSet<&str> = HashSet::new();
sold_out.insert("peras");
println!("manzanas: {}, agotados: {}", stock["manzanas"], sold_out.len());
}

Fíjate en que el help es distinto: nada de as, solo “remove unnecessary import” (quita el import innecesario). Deja una sola línea:

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

Salida:

manzanas: 3, agotados: 1
▶ Pruébalo tú mismo

Mira HashMaps y HashSet.

Error desde dos sitios. use std::fmt::Error; y use std::io::Error;, o use std::error::Error; al lado de use anyhow::Error;. Solución: use std::io; y escribe io::Error, o use anyhow::Error as AnyError;. Mira anyhow y thiserror.

Dos crates con el mismo nombre de tipo. use chrono::Duration; y use std::time::Duration; son una pareja habitual en proyectos que manejan fechas. Solución: deja un nombre corto y escribe el módulo del otro, como chrono::Duration. Mira Fechas y horas.

Imports glob (con comodín). use std::fmt::*; junto con use std::io::*; no da E0252 en las líneas use. El problema llega después, en el primer Result, como un error sin código: error: `Result` is ambiguous (Result es ambiguo). Solución: importa de forma explícita los nombres que necesitas, como en la Solución 1.

  • E0255: una línea use choca con un struct, enum o función tuyos del mismo nombre.
  • E0428: dos elementos tuyos comparten nombre, a menudo al intentar sobrecargar una función.
  • E0432: import sin resolver, cuando la línea use apunta a algo que no existe.
  • E0034: dos traits en el ámbito le dan a un tipo métodos con el mismo nombre.
  • E0308: tipos que no coinciden, uno de los errores que vienen después de un choque de Result.
  • Todos los errores del compilador: la lista completa de páginas de errores.

Comprueba lo aprendido

1. Necesitas fmt::Result e io::Result en el mismo archivo. ¿Qué imports son la opción limpia habitual?

2. Necesitas los métodos de std::fmt::Write y de std::io::Write, pero nunca escribes tú los nombres de los traits. ¿Qué funciona?

3. rustc dice que HashMap is reimported here y sugiere remove unnecessary import. ¿Qué pasó?

Puntuación: 0 / 3