Pular para o conteúdo

Erro E0255 do Rust: the name is defined multiple times (import vs item local)

O E0255 quer dizer que uma linha use e algo que você mesmo definiu têm o mesmo nome. O caso clássico: você importa std::io::Error no topo do arquivo e, mais abaixo, escreve o seu próprio enum Error. O Rust não deixa um nome significar duas coisas, então ele para. É um erro muito comum quando você começa a escrever os seus próprios tipos de erro, e a correção é uma linha curta. Ao contrário do E0412, este código não mudou nas versões recentes: o rustc 1.97 continua mostrando E0255. Esta página faz parte do guia de erros do compilador.

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` here
2 |
3 | enum Error { // ❌ mesmo nome do import da linha 1
| ^^^^^^^^^^ `Error` redefined here
|
= note: `Error` 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
|
1 | use std::io::Error as OtherError;
| +++++++++++++
warning: unused import: `std::io::Error`
--> src/main.rs:1:5
|
1 | use std::io::Error;
| ^^^^^^^^^^^^^^

Em português: “o nome Error foi definido várias vezes”. O rustc mostra o import anterior, a redefinição, uma nota sobre o namespace de tipos, uma sugestão com as e, no fim, um aviso de import não usado.

Este é o programa que gera o erro:

use std::io::Error;
enum Error { // ❌ mesmo nome do import da linha 1
EmptyName,
TooLong(usize),
}
fn check_name(name: &str) -> Result<(), Error> {
if name.is_empty() {
return Err(Error::EmptyName);
}
if name.len() > 10 {
return Err(Error::TooLong(name.len()));
}
Ok(())
}
fn main() {
match check_name("") {
Ok(()) => println!("o nome está ok"),
Err(Error::EmptyName) => println!("o nome está vazio"),
Err(Error::TooLong(n)) => println!("o nome tem {} letras, o máximo é 10", n),
}
}

O rustc aponta para três coisas:

  1. A linha --- (linha 1): “previous import of the type Error here” (import anterior do tipo Error aqui). A linha use já colocou um nome Error neste módulo.
  2. A linha ^^^ (linha 3): “Error redefined here” (Error redefinido aqui). O seu enum Error tenta ocupar o mesmo nome.
  3. O help: oferece uma saída: dar ao import outro nome local com as.

O warning: unused import (import não usado) logo abaixo é uma pista útil. Ele quer dizer que nada no seu código usa de verdade o std::io::Error (todo Error neste programa é o seu enum). Nesse caso, você pode simplesmente apagar a linha 1. Se você precisa dos dois, continue lendo.

A nota fala do type namespace (namespace de tipos). Tipos, traits e módulos dividem uma lista de nomes; funções, constantes e statics dividem outra. Por isso use std::mem::swap; mais uma fn swap sua dá o mesmo erro, só que no “value namespace” (namespace de valores).

Pense numa sala de aula com dois alunos chamados Alex. A professora não pode mais só chamar “Alex”. Alguém precisa de um apelido, como “Alex B.”, e é exatamente isso que o as dá a um import.

Um nome num módulo aponta para exatamente um item. Quando o Rust lê Error::EmptyName na linha 10, ele precisa saber de qual Error você está falando, e se recusa a adivinhar. Se ele escolhesse o seu enum em silêncio, a sua linha use não faria nada; se escolhesse o io::Error, o seu enum ficaria inacessível. De um jeito ou de outro, uma escolha silenciosa esconderia um engano.

É por isso que o E0255 é irmão do E0428 (dois itens seus com o mesmo nome) e do E0252 (duas linhas use com o mesmo nome). A regra é a mesma: um nome, uma coisa, por módulo. Veja em Módulos como o use funciona.

Escolha esta quando você precisa dos dois tipos no mesmo arquivo, o que é comum: o seu próprio Error para as regras do seu programa e o io::Error para arquivos e rede. Dê ao import um apelido que diga de onde ele vem. IoError fica mais claro que o OtherError provisório do rustc:

use std::io::Error as IoError;
use std::io::ErrorKind;
#[derive(Debug)]
enum Error {
EmptyName,
TooLong(usize),
}
fn check_name(name: &str) -> Result<(), Error> {
if name.is_empty() {
return Err(Error::EmptyName);
}
if name.len() > 10 {
return Err(Error::TooLong(name.len()));
}
Ok(())
}
fn save_name(name: &str) -> Result<(), IoError> {
if name == "root" {
return Err(IoError::new(ErrorKind::PermissionDenied, "esse nome é reservado"));
}
Ok(())
}
fn main() {
println!("{:?}", check_name("Ferris"));
println!("{:?}", check_name(""));
println!("{:?}", check_name("Ferris the Crab"));
match save_name("root") {
Ok(()) => println!("salvo"),
Err(e) => println!("erro de E/S: {}", e),
}
}

Saída:

Ok(())
Err(EmptyName)
Err(TooLong(15))
erro de E/S: esse nome é reservado
▶ Experimente você mesmo

O as só muda o nome neste arquivo. Em todos os outros lugares, o tipo continua sendo std::io::Error.

Escolha esta quando você quer que cada Error mostre de onde vem em todo lugar em que aparece. Importe o módulo pai com use std::io; e escreva io::Error. O seu próprio Error fica com o nome curto. Muitos programadores Rust preferem esse estilo para tipos com nomes comuns como Error e Result:

use std::io;
enum Error {
EmptyName,
}
fn check_name(name: &str) -> Result<(), Error> {
if name.is_empty() {
return Err(Error::EmptyName);
}
Ok(())
}
fn save_name(name: &str) -> Result<(), io::Error> {
if name == "root" {
return Err(io::Error::new(io::ErrorKind::PermissionDenied, "nome reservado"));
}
Ok(())
}
fn main() {
if let Err(Error::EmptyName) = check_name("") {
println!("o nome está vazio");
}
if let Err(e) = save_name("root") {
println!("erro de E/S: {}", e);
}
}

Saída:

o nome está vazio
erro de E/S: nome reservado
▶ Experimente você mesmo

Escolha esta quando o seu tipo vai ser usado em muitos arquivos, ou por outras pessoas. Um nome específico como NameError ou AppError é mais claro que um segundo Error, e a linha use pode ficar como está:

use std::io::Error;
#[derive(Debug)]
enum NameError {
EmptyName,
TooLong(usize),
}
fn check_name(name: &str) -> Result<(), NameError> {
if name.is_empty() {
return Err(NameError::EmptyName);
}
if name.len() > 10 {
return Err(NameError::TooLong(name.len()));
}
Ok(())
}
fn main() {
println!("{:?}", check_name("Ferris the Crab"));
let disk = Error::other("disco cheio");
println!("{}", disk);
}

Saída:

Err(TooLong(15))
disco cheio
▶ Experimente você mesmo

Em projetos maiores, o crate thiserror deixa esses enums de erro bem mais curtos de escrever. Veja Erros personalizados.

Escolha esta quando o rustc também mostra warning: unused import, como no erro acima. O import sobrou de um código antigo, ou foi colocado pelo seu editor. Tire a linha, e o programa do começo desta página funciona como foi escrito:

enum Error {
EmptyName,
TooLong(usize),
}
fn check_name(name: &str) -> Result<(), Error> {
if name.is_empty() {
return Err(Error::EmptyName);
}
if name.len() > 10 {
return Err(Error::TooLong(name.len()));
}
Ok(())
}
fn main() {
match check_name("") {
Ok(()) => println!("o nome está ok"),
Err(Error::EmptyName) => println!("o nome está vazio"),
Err(Error::TooLong(n)) => println!("o nome tem {} letras, o máximo é 10", n),
}
}

Saída:

o nome está vazio
▶ Experimente você mesmo

Uma função sua com um nome da std. use std::mem::swap; mais fn swap(a: &mut i32, b: &mut i32) dá E0255 com “previous import of the value swap here” (import anterior do valor swap aqui) e a nota “value namespace”. Correção: chame a sua de swap_values, ou tire o import se você queria mesmo escrever a sua.

Copiar e colar entre módulos. Você move a struct User para dentro de mod models, acrescenta use models::User; no topo, mas esquece de apagar a struct User antiga no main.rs. Correção: apague a cópia antiga, para que User signifique só a de models.

Um trait com um nome da std. use std::fmt::Display; e um trait Display seu se chocam do mesmo jeito (“previous import of the trait Display”). Correção: renomeie o seu trait (Describe, Render), ou escreva use std::fmt; e fmt::Display.

Bom saber: um import glob como use std::io::*; nunca causa E0255. Os seus próprios itens ganham, sem aviso, dos nomes que entram por um *. Isso é prático, mas também pode esconder qual Error você está usando, então imports explícitos costumam ser mais claros.

  • E0252: duas linhas use trazem o mesmo nome, como fmt::Result e io::Result.
  • E0428: dois itens seus têm o mesmo nome, muitas vezes por tentar sobrecarregar uma função.
  • E0592: dois métodos com o mesmo nome num bloco impl.
  • E0432: a própria linha use aponta para algo que não existe.
  • E0425: o problema contrário: um nome que não está definido onde você o usa.
  • Todos os erros do compilador: a lista completa de páginas de erro.

Teste o que aprendeu

1. O seu arquivo começa com use std::fmt::Result; e mais abaixo define enum Result { Pass, Fail }. Qual erro você recebe?

2. Você precisa do std::io::Error e do seu próprio enum Error no mesmo arquivo. Qual é uma correção limpa?

3. O rustc mostra E0255 e também warning: unused import: std::io::Error. O que o aviso diz?

Pontuação: 0 / 3