Erro E0428 do Rust: the name is defined multiple times (nome repetido)
O E0428 quer dizer que você definiu duas coisas com o mesmo nome no mesmo lugar: duas funções chamadas area, duas structs chamadas User, ou um enum e uma struct chamados Status. Isso acontece muito quando você tenta fazer sobrecarga (overloading) de uma função, como faria em Java, C# ou C++. O Rust não tem sobrecarga, mas tem bons substitutos, e esta página mostra quais são. Ela faz parte do guia de erros do compilador.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0428]: the name `area` is defined multiple times --> src/main.rs:5:1 |1 | fn area(width: f64, height: f64) -> f64 { | --------------------------------------- previous definition of the value `area` here...5 | fn area(radius: f64) -> f64 { // ❌ uma segunda `area` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `area` redefined here | = note: `area` must be defined only once in the value namespace of this moduleEm português: “o nome area foi definido várias vezes”. O rustc mostra a definição anterior, a nova definição e uma nota dizendo que area só pode ser definido uma vez no namespace de valores deste módulo.
Este é o programa que gera o erro:
fn area(width: f64, height: f64) -> f64 { width * height}
fn area(radius: f64) -> f64 { // ❌ uma segunda `area` 3.14159 * radius * radius}
fn main() { println!("{}", area(2.0, 3.0)); println!("{}", area(1.0));}O rustc também mostra um segundo erro para este programa: o E0061, “this function takes 2 arguments but 1 argument was supplied” (esta função recebe 2 argumentos, mas foi passado 1), em area(1.0). Isso é só um efeito colateral. O Rust ficou com a primeira area e ignorou a segunda, então a chamada com um argumento não encaixa mais. Corrija o E0428 e o E0061 some junto.
O que significa
Seção intitulada “O que significa”O rustc aponta para dois lugares:
- A linha
---(linha 1): “previous definition of the valueareahere” (definição anterior do valorareaaqui). Esta é a primeiraarea, a que o Rust mantém. - A linha
^^^(linha 5): “arearedefined here” (arearedefinida aqui). Esta é a segunda, a que é rejeitada. - A
note:diz queareasó pode ser definida uma vez “in the value namespace of this module” (no namespace de valores deste módulo). Cada módulo é como uma gaveta, e dentro de uma gaveta cada nome precisa ser único.
Para duas structs, a nota diz “type namespace” (namespace de tipos). O Rust guarda os tipos (structs, enums, traits, módulos) e os valores (funções, constantes, statics) em duas listas separadas. É por isso que um mod shapes e uma fn shapes podem conviver lado a lado, mas duas fn area ou duas struct User não podem.
Pense em dois arquivos chamados relatorio.txt na mesma pasta. O seu computador recusa o segundo, não porque haja algo errado com ele, mas porque relatorio.txt deixaria de dizer de qual arquivo você está falando.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Em Rust, um nome num módulo aponta para exatamente um item. Quando você escreve area(...), o Rust encontra a função só pelo nome, e não pela quantidade ou pelos tipos dos argumentos. Isso deixa o código simples de ler: só existe uma area para procurar, e acrescentar uma função nova nunca muda em silêncio qual função uma chamada antiga usa.
Linguagens com sobrecarga escolhem a versão pelos argumentos, e as regras ficam complicadas quando números se convertem uns nos outros ou quando entram valores padrão. O Rust escolheu não fazer isso. Quando você quer “o mesmo nome para entradas diferentes”, usa traits ou genéricos, onde a escolha fica visível nos tipos. Mais sobre funções em Funções.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Dê um nome próprio a cada uma
Seção intitulada “Correção 1: Dê um nome próprio a cada uma”Escolha esta na maioria das vezes. Duas funções que recebem entradas diferentes normalmente fazem trabalhos diferentes, e o nome pode dizer qual. Se uma delas era uma cópia antiga que você esqueceu de apagar, apague-a:
fn rectangle_area(width: f64, height: f64) -> f64 { width * height}
fn circle_area(radius: f64) -> f64 { 3.14159 * radius * radius}
fn main() { println!("{}", rectangle_area(2.0, 3.0)); println!("{}", circle_area(1.0));}Saída:
63.14159A biblioteca padrão faz o mesmo: String::new(), String::with_capacity(10) e String::from("oi") são três nomes diferentes para três jeitos de criar uma String.
Correção 2: Coloque cada uma num módulo separado
Seção intitulada “Correção 2: Coloque cada uma num módulo separado”Escolha esta quando o nome curto é mesmo o melhor nome em cada contexto, como area para cada forma, ou um User no código do banco de dados e outro User no código da API. Cada módulo tem os seus próprios nomes, então o conflito desaparece. Quem chama escreve o nome do módulo na frente:
mod rectangle { pub fn area(width: f64, height: f64) -> f64 { width * height }}
mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius }}
fn main() { println!("{}", rectangle::area(2.0, 3.0)); println!("{}", circle::area(1.0));}Saída:
63.14159Não esqueça o pub, senão a chamada no main dá E0603, “function is private” (a função é privada). Veja Módulos.
Correção 3: Use um trait em vez de sobrecarga
Seção intitulada “Correção 3: Use um trait em vez de sobrecarga”Escolha esta quando você quer uma chamada que funcione para vários tipos, que é para isso que a sobrecarga costuma servir. Crie um tipo para cada tipo de entrada e dê a eles um trait em comum. Agora cada forma tem a sua própria area, e o Rust escolhe a certa pelo tipo do valor:
trait Shape { fn area(&self) -> f64;}
struct Rectangle { width: f64, height: f64,}
struct Circle { radius: f64,}
impl Shape for Rectangle { fn area(&self) -> f64 { self.width * self.height }}
impl Shape for Circle { fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }}
fn print_area<T: Shape>(shape: &T) { println!("Área: {}", shape.area());}
fn main() { print_area(&Rectangle { width: 2.0, height: 3.0 }); print_area(&Circle { radius: 1.0 });}Saída:
Área: 6Área: 3.14159Isso dá mais código que a Correção 1, então vale a pena quando você tem várias formas ou várias funções (area, perimeter, draw) que seguem o mesmo padrão. A função genérica print_area<T: Shape> funciona para qualquer forma que você acrescentar depois. Veja Traits e Genéricos.
Para “a mesma função com ou sem um argumento a mais”, existe uma opção mais leve: receba um Option, como em fn greet(name: &str, title: Option<&str>), e chame com None ou Some("Dr.").
Correção 4: Renomeie um import com use ... as ...
Seção intitulada “Correção 4: Renomeie um import com use ... as ...”Escolha esta quando o conflito é entre um item seu e uma linha use. A rigor, esse é um erro irmão, o E0255, com a mesma primeira linha:
error[E0255]: the name `Error` is defined multiple times --> src/main.rs:3:1 |1 | use std::io::Error; | -------------- previous import of the type `Error` here2 |3 | enum Error { | ^^^^^^^^^^ `Error` redefined here | = note: `Error` must be defined only once in the type namespace of this modulehelp: you can use `as` to change the binding name of the import |1 | use std::io::Error as OtherError; | +++++++++++++O rustc mostra o import anterior do tipo Error, a redefinição, e a ajuda diz “você pode usar as para mudar o nome do import”.
Dê ao import um apelido local com as. Escolha um nome que diga de onde ele vem, como IoError, em vez do OtherError provisório do rustc:
use std::io::Error as IoError;use std::io::ErrorKind;
#[derive(Debug)]enum Error { NotFound(String), Invalid(u32),}
fn main() { let mine = Error::NotFound(String::from("config.toml")); let other = Error::Invalid(42); let disk = IoError::new(ErrorKind::Other, "disco cheio");
println!("{:?}", mine); println!("{:?}", other); println!("{}", disk);}Saída:
NotFound("config.toml")Invalid(42)disco cheioDuas linhas use que se chocam, como use std::fmt::Result; e use std::io::Result;, dão um terceiro irmão, o E0252 (`Result` reimported here, Result importado de novo aqui). A correção é a mesma: use std::io::Result as IoResult;, ou importe o módulo (use std::io;) e escreva io::Result. Renomear o seu próprio enum (AppError) também funciona.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Sobrecarregar um método num bloco impl. Duas fn new em impl Timer dão outro código, o E0592: duplicate definitions with name `new` (definições duplicadas com o nome new). Mesma causa, mesma correção: dê nomes que digam o que cada uma faz, como Timer::new() e Timer::with_seconds(30). Veja Structs.
Copiar e colar durante uma refatoração. Você copia uma função para alterá-la, ou cola uma struct User de outro arquivo, e esquece de tirar a antiga. Também é comum em testes: duas #[test] fn it_works() no mesmo mod tests dão E0428. Correção: apague a cópia antiga, ou renomeie o teste.
Variantes de enum repetidas. enum Color { Red, Red } também dá E0428, apontando para o segundo Red, com a nota “type namespace of this enum” (namespace de tipos deste enum). Correção: tire ou renomeie a variante repetida. Veja Enums.
Erros relacionados
Seção intitulada “Erros relacionados”- E0061: número errado de argumentos. Muitas vezes aparece logo depois do E0428 quando você tentou sobrecarregar uma função.
- E0425: o problema contrário: um nome que está definido zero vezes onde você o usa.
- E0433: um caminho como
circle::areacuja primeira parte (o módulo) não foi encontrada. - E0034: dois traits dão a um tipo métodos com o mesmo nome; você precisa dizer qual deles quer.
- E0255 (um
usese choca com um item local), E0252 (duas linhasusese chocam), E0592 (método repetido numimpl): os irmãos mostrados acima. - Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Você escreve fn total(a: u32) e fn total(a: u32, b: u32) no mesmo arquivo. O que acontece?
2. mod report e fn report() podem ficar no mesmo módulo?
3. O seu arquivo tem use std::fmt::Result; e você define enum Result. Qual é uma correção limpa?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0428 — Rust error codes index (em inglês): a explicação oficial, com o exemplo de duas structs.
- Defining Modules to Control Scope and Privacy — The Rust Book (em inglês): como os módulos dão a cada parte do seu código os seus próprios nomes.
- Providing New Names with the
asKeyword — The Rust Book (em inglês): o exemplo defmt::Resulteio::Result, resolvido comas. - Traits — The Rust Book (em inglês): como compartilhar comportamento entre tipos, a resposta do Rust à sobrecarga.
- Namespaces — The Rust Reference (em inglês): as regras exatas dos namespaces de tipos e de valores citados na nota do erro.