Ir al contenido

Error E0592 de Rust: duplicate definitions with name X (método duplicado)

E0592 significa: el mismo tipo tiene dos métodos, funciones asociadas o constantes con el mismo nombre, escritos en bloques impl. Un tipo puede tener todos los bloques impl que quieras, pero todos comparten una sola lista de nombres, así que cada nombre se puede usar una sola vez. Suele venir de copiar y pegar, de un merge, o de un segundo bloque impl en otro archivo. La solución es quedarte con uno, o darle al otro un nombre nuevo. (Esta página forma parte de Errores del compilador de Rust explicados.)

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` is already defined above
| ------------------- other definition for `reset`

“Duplicate definitions with name reset” quiere decir “definiciones duplicadas con el nombre reset”. La etiqueta ^^^ marca una de ellas, y la etiqueta --- dice “other definition for reset” (otra definición de reset).

Este es el programa que lo produce:

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

rustc también informa de E0034, “multiple applicable items in scope” (varios elementos aplicables en el ámbito), en la llamada c.reset(), porque no sabe a cuál de los dos reset te refieres. Es un error derivado: arregla E0592 y E0034 desaparece también.

El titular te da el nombre que está definido dos veces (reset). El ^^^ marca una definición, y la línea --- marca la “otra definición”. rustc te muestra los números de línea de las dos, así que puedes ir directo a ellas. Cuando están lejos, los ... de en medio solo significan “se saltaron algunas líneas”.

Da igual que los dos reset estén en bloques impl Counter distintos, o incluso en archivos distintos de tu crate. Para Rust todos son métodos del mismo tipo, así que Counter::reset tiene que significar exactamente una función. La misma regla vale para las constantes asociadas: dos const MAX: u32 en un mismo tipo también dan E0592.

Es como tener en la agenda del móvil dos contactos llamados “Mamá”. Cuando dices “llama a Mamá”, ¿qué número debería marcar?

Repartir los métodos en varios bloques impl está permitido y a menudo es útil: un bloque para los constructores, otro para los getters, otro detrás de un #[cfg(test)]. Mira Añadir comportamiento con métodos. Pero cuando llamas a c.reset() o a Counter::reset(&mut c), Rust tiene que encontrar una función. Si hubiera dos, cuál se ejecuta dependería de detalles que nadie ve al leer la llamada.

Por eso Rust lo rechaza en la definición, no solo en la llamada. Así atrapa un bug real: con un método copiado y pegado podrías arreglar un problema en una copia mientras la otra sigue ejecutándose. Rust se asegura de que siempre haya una sola versión que arreglar.

Solución 1: Borra el duplicado y quédate con una definición

Sección titulada «Solución 1: Borra el duplicado y quédate con una definición»

Elige esta la mayoría de las veces. Si las dos copias hacen lo mismo (copiar y pegar, un merge desordenado, un método que moviste pero no borraste), borra una. Antes de borrar, compara los dos cuerpos: si uno tiene un arreglo que el otro no tiene, quédate con ese. Juntar los bloques impl en uno también hace que los duplicados sean más fáciles de ver en el futuro.

struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Counter { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
fn reset(&mut self) { // ✅ un solo `reset` para `Counter`
self.count = 0;
}
}
fn main() {
let mut c = Counter::new();
c.increment();
c.increment();
println!("antes de reset: {}", c.count);
c.reset();
println!("después de reset: {}", c.count);
}

Salida:

antes de reset: 2
después de reset: 0
▶ Pruébalo tú mismo

Si no encuentras la segunda definición, busca en todo el proyecto: grep -rn "fn reset" src/. Un bloque impl Counter puede estar en cualquier módulo de tu crate.

Solución 2: Cambia el nombre de uno de ellos

Sección titulada «Solución 2: Cambia el nombre de uno de ellos»

Elige esta cuando las dos definiciones hacen cosas distintas. Rust no tiene sobrecarga (overloading): no puedes tener dos métodos con el mismo nombre y parámetros distintos, como reset() y reset(start: u32). Eso también es E0592. Dale a cada uno un nombre que diga lo que hace:

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) { // ✅ un nombre distinto para un comportamiento distinto
self.count = start;
}
}
fn main() {
let mut c = Counter::new();
c.reset_to(10);
println!("después de reset_to(10): {}", c.count);
c.reset();
println!("después de reset(): {}", c.count);
}

Salida:

después de reset_to(10): 10
después de reset(): 0
▶ Pruébalo tú mismo

Los nombres habituales en Rust te ayudan aquí: new y with_capacity, from_str y from_bytes, get y get_mut. Si de verdad quieres un solo nombre para varios tipos de argumento, usa un parámetro genérico o un trait, como fn reset_to(&mut self, start: impl Into<u32>). Mira Genéricos.

Solución 3: Evita que los bloques impl genéricos se solapen

Sección titulada «Solución 3: Evita que los bloques impl genéricos se solapen»

Elige esta cuando una de las dos definiciones está en un bloque genérico como impl<T> Wrapper<T>. Ese bloque también cubre Wrapper<i32>, así que un segundo impl Wrapper<i32> con el mismo nombre de método es un duplicado:

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`

Es el mismo mensaje de antes: dos definiciones de describe, una en la línea 4 (el bloque genérico) y otra en la línea 10 (el bloque para i32).

El programa que hay detrás:

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

Rust no deja que un impl concreto reemplace a uno genérico (esa función, llamada specialization o especialización, no es estable). Hay dos buenas salidas: escribir bloques impl solo para tipos concretos, para que no puedan solaparse, o poner el comportamiento en un trait e implementarlo una vez por tipo:

struct Wrapper<T>(T);
// Opción A: los bloques impl para tipos concretos nunca se solapan
impl Wrapper<i32> {
fn describe(&self) -> String {
format!("el número {}", self.0)
}
}
impl Wrapper<&str> {
fn describe(&self) -> String {
format!("el texto \"{}\"", self.0)
}
}
// Opción B: un trait, implementado una vez por tipo
trait Label {
fn label(&self) -> String;
}
impl Label for i32 {
fn label(&self) -> String {
format!("entero {}", self)
}
}
impl Label for bool {
fn label(&self) -> String {
format!("booleano {}", self)
}
}
impl<T: Label> Wrapper<T> {
fn label(&self) -> String {
self.0.label() // un solo método genérico; la parte de cada tipo vive en el trait
}
}
fn main() {
println!("{}", Wrapper(5).describe());
println!("{}", Wrapper("hola").describe());
println!("{}", Wrapper(7).label());
println!("{}", Wrapper(true).label());
}

Salida:

el número 5
el texto "hola"
entero 7
booleano true
▶ Pruébalo tú mismo

La versión con trait suele ser el mejor diseño a largo plazo: dar soporte a un tipo nuevo es solo un impl Label for ... más. Mira Traits.

Una función auxiliar de tests en un segundo bloque impl. Añades impl Counter { fn new() -> Self { ... } } dentro de #[cfg(test)] mod tests para crear valores de prueba, pero Counter::new ya existe. El bloque de los tests sigue siendo un impl Counter. Solución: ponle un nombre como fn for_test(), o usa el new de verdad. Mira Tests.

Después de un merge o una refactorización. Dos ramas añadieron cada una un fn len(&self), o moviste un método a un archivo nuevo y la copia antigua se quedó. rustc te muestra los dos números de línea; quédate con el mejor.

Un método de trait con el mismo nombre no es un problema. impl Counter { fn reset(&mut self) } junto con impl Resettable for Counter { fn reset(&mut self) } no es E0592: uno es un método propio del tipo, el otro pertenece al trait, y c.reset() llama al propio del tipo. Dos traits con el mismo nombre de método también compilan, pero llamarlo con c.reset() da E0034; arréglalo con Resettable::reset(&mut c).

Comprueba lo aprendido

1. Un tipo tiene dos bloques impl. ¿Pueden los dos tener un método llamado reset?

2. Quieres area() y area(scale: f64). ¿Qué haces en Rust?

3. ¿Por qué chocan impl<T> Wrapper<T> e impl Wrapper<i32> cuando los dos definen describe?

Puntuación: 0 / 3