Erro E0592 do Rust: duplicate definitions with name X (método duplicado)
O E0592 quer dizer: o mesmo tipo tem dois métodos, funções associadas ou constantes com o mesmo nome, escritos em blocos impl. Um tipo pode ter quantos blocos impl você quiser, mas todos eles compartilham uma única lista de nomes, então cada nome só pode ser usado uma vez. Normalmente isso vem de um copiar e colar, de um merge, ou de um segundo bloco impl em outro arquivo. A correção é manter um só, ou dar um nome novo ao outro. (Esta página faz parte de Erros do compilador Rust explicados.)
A mensagem de erro
Seção intitulada “A mensagem de erro”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` já foi definido acima | ------------------- other definition for `reset`Em português: “definições duplicadas com o nome reset”. O ^^^ marca uma definição duplicada de reset, e o --- marca a “outra definição” de reset.
Este é o programa que gera o erro:
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` já foi definido acima self.count = 0; }}
fn main() { let mut c = Counter::new(); c.reset(); println!("{}", c.count);}O rustc também mostra o E0034, “multiple applicable items in scope” (vários itens aplicáveis no escopo), na chamada c.reset(), porque não consegue saber qual dos dois reset você quer. É um erro de consequência: corrija o E0592 e o E0034 some junto.
O que significa
Seção intitulada “O que significa”A primeira linha diz o nome que foi definido duas vezes (reset). O ^^^ marca uma definição, e a linha --- marca a “outra definição”. O rustc mostra o número da linha das duas, então você pode pular direto para elas. Quando estão longe uma da outra, o ... no meio só quer dizer “algumas linhas foram puladas”.
Não faz diferença que os dois reset estejam em blocos impl Counter diferentes, ou até em arquivos diferentes do seu crate. Para o Rust, todos são métodos do mesmo tipo, então Counter::reset precisa significar exatamente uma função. A mesma regra vale para constantes associadas: duas const MAX: u32 para um tipo também dão E0592.
É como ter dois contatos chamados “Mãe” na agenda do celular. Quando você diz “ligar para a Mãe”, para qual número ele deveria ligar?
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Dividir os métodos em vários blocos impl é permitido e muitas vezes útil: um bloco para construtores, um para getters, um atrás de um #[cfg(test)]. Veja Adicionando comportamento com métodos. Mas quando você chama c.reset() ou Counter::reset(&mut c), o Rust precisa encontrar uma função. Se houvesse duas, qual delas roda dependeria de detalhes que ninguém enxerga lendo a chamada.
Por isso o Rust recusa já na definição, e não só na chamada. Isso pega um bug de verdade: com um método copiado e colado, você pode corrigir um problema numa cópia, e a outra continua rodando. O Rust garante que sempre exista uma única versão para corrigir.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Apague a duplicata e mantenha uma definição só
Seção intitulada “Correção 1: Apague a duplicata e mantenha uma definição só”Escolha esta na maioria das vezes. Se as duas cópias fazem a mesma coisa (copiar e colar, um merge bagunçado, um método que você moveu mas não apagou), apague uma. Antes de apagar, compare os dois corpos: se um tem uma correção que o outro não tem, fique com esse. Juntar os blocos impl num só também deixa as duplicatas mais fáceis de ver no futuro.
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Counter { count: 0 } }
fn increment(&mut self) { self.count += 1; }
fn reset(&mut self) { // ✅ um único `reset` para `Counter` self.count = 0; }}
fn main() { let mut c = Counter::new(); c.increment(); c.increment(); println!("antes do reset: {}", c.count); c.reset(); println!("depois do reset: {}", c.count);}Saída:
antes do reset: 2depois do reset: 0Se você não encontra a segunda definição, procure no projeto inteiro: grep -rn "fn reset" src/. Um bloco impl Counter pode estar em qualquer módulo do seu crate.
Correção 2: Renomeie uma delas
Seção intitulada “Correção 2: Renomeie uma delas”Escolha esta quando as duas definições fazem coisas diferentes. O Rust não tem sobrecarga (overloading): você não pode ter dois métodos com o mesmo nome e parâmetros diferentes, como reset() e reset(start: u32). Isso também é E0592. Dê a cada um um nome que diga o que ele faz:
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) { // ✅ um nome diferente para um comportamento diferente self.count = start; }}
fn main() { let mut c = Counter::new(); c.reset_to(10); println!("depois de reset_to(10): {}", c.count); c.reset(); println!("depois de reset(): {}", c.count);}Saída:
depois de reset_to(10): 10depois de reset(): 0Os padrões de nomes comuns em Rust ajudam aqui: new e with_capacity, from_str e from_bytes, get e get_mut. Se você quer mesmo um nome só para vários tipos de argumento, use um parâmetro genérico ou um trait, como fn reset_to(&mut self, start: impl Into<u32>). Veja Genéricos.
Correção 3: Evite que blocos impl genéricos se sobreponham
Seção intitulada “Correção 3: Evite que blocos impl genéricos se sobreponham”Escolha esta quando uma das duas definições está num bloco genérico como impl<T> Wrapper<T>. Esse bloco também cobre Wrapper<i32>, então um segundo impl Wrapper<i32> com o mesmo nome de método é uma duplicata:
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`É a mesma mensagem: definições duplicadas de describe, uma na linha 4 e a outra na linha 10.
O programa por trás dele:
struct Wrapper<T>(T);
impl<T> Wrapper<T> { fn describe(&self) -> String { String::from("alguma coisa") }}
impl Wrapper<i32> { fn describe(&self) -> String { format!("o número {}", self.0) }}
fn main() { println!("{}", Wrapper(5).describe());}O Rust não deixa um impl específico substituir um genérico (esse recurso, chamado specialization, ainda não é estável). Há duas boas saídas: escreva blocos impl só para tipos específicos, para que eles não possam se sobrepor, ou coloque o comportamento num trait e implemente uma vez para cada tipo:
struct Wrapper<T>(T);
// Opção A: blocos impl para tipos específicos nunca se sobrepõemimpl Wrapper<i32> { fn describe(&self) -> String { format!("o número {}", self.0) }}
impl Wrapper<&str> { fn describe(&self) -> String { format!("o texto \"{}\"", self.0) }}
// Opção B: um trait, implementado uma vez para cada tipotrait Label { fn label(&self) -> String;}
impl Label for i32 { fn label(&self) -> String { format!("inteiro {}", self) }}
impl Label for bool { fn label(&self) -> String { format!("booleano {}", self) }}
impl<T: Label> Wrapper<T> { fn label(&self) -> String { self.0.label() // um método genérico; a parte de cada tipo fica no trait }}
fn main() { println!("{}", Wrapper(5).describe()); println!("{}", Wrapper("oi").describe()); println!("{}", Wrapper(7).label()); println!("{}", Wrapper(true).label());}Saída:
o número 5o texto "oi"inteiro 7booleano trueA versão com trait costuma ser o melhor design a longo prazo: dar suporte a um tipo novo é só mais um impl Label for .... Veja Traits.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Uma função auxiliar de teste num segundo bloco impl. Você acrescenta impl Counter { fn new() -> Self { ... } } dentro de #[cfg(test)] mod tests para montar valores de teste, mas Counter::new já existe. O bloco de teste continua sendo um impl Counter. Correção: dê um nome como fn for_test(), ou use o new de verdade. Veja Testes.
Depois de um merge ou de uma refatoração. Duas branches acrescentaram um fn len(&self) cada uma, ou um método foi movido para um arquivo novo e a cópia antiga ficou. O rustc mostra os dois números de linha; fique com a melhor versão.
Um método de trait com o mesmo nome não tem problema. impl Counter { fn reset(&mut self) } junto com impl Resettable for Counter { fn reset(&mut self) } não é E0592: um é o método próprio do tipo, o outro pertence ao trait, e c.reset() chama o método próprio do tipo. Dois traits com o mesmo nome de método também compilam, mas chamar com c.reset() dá E0034; corrija isso com Resettable::reset(&mut c).
Erros relacionados
Seção intitulada “Erros relacionados”- E0034: multiple applicable items in scope: o erro de consequência no lugar da chamada, e o erro para dois traits com o mesmo nome de método.
- E0428: the name is defined multiple times: o mesmo engano para itens num módulo, como duas
struct Counterou duas funções soltasfn main. - E0599: no method named x found: o problema contrário, um método que nem existe.
- E0255: nome já usado por um import e E0252: nome importado duas vezes: nomes duplicados causados pelo
use. - Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Um tipo tem dois blocos impl. Os dois podem ter um método chamado reset?
2. Você quer area() e area(scale: f64). O que fazer em Rust?
3. Por que impl<T> Wrapper<T> e impl Wrapper<i32> se chocam quando os dois definem describe?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0592 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Method Syntax — The Rust Book (em inglês): métodos, funções associadas e a seção “Multiple
implBlocks”. - Generic Data Types — The Rust Book (em inglês):
impl<T> Point<T>ao lado deimpl Point<f32>, e como os dois se relacionam. - Implementations — The Rust Reference (em inglês): as regras exatas dos blocos
implpróprios de um tipo e dos seus itens associados. - E0034 — Rust error codes index (em inglês): a página oficial do erro de consequência “multiple applicable items”.