Pular para o conteúdo

Erro E0252 do Rust: the name is defined multiple times (dois imports)

O E0252 quer dizer que duas linhas use trazem o mesmo nome. O caso de livro é use std::fmt::Result; mais use std::io::Result;: dois tipos diferentes, os dois chamados Result. Outros exemplos são fmt::Error e io::Error, ou os dois traits Write. É um erro muito comum e fácil de corrigir, normalmente mudando uma ou duas linhas use. O rustc 1.97 continua mostrando esse erro com o seu próprio código, E0252. Esta página faz parte do guia de erros do 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; // ❌ um segundo `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; // ❌ um segundo `Result`
| ++++++++++++++

Em português: “o nome Result foi definido várias vezes”. O rustc mostra o primeiro import, o segundo (“importado de novo aqui”), uma nota sobre o namespace de tipos e uma sugestão com as.

Este é o programa que gera o erro:

use std::fmt::Result;
use std::io::Result; // ❌ um segundo `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("não é um número")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("erro: {}", e),
}
}

O rustc mostra mais quatro erros depois deste: E0107 (“type alias takes 0 generic arguments”, o alias de tipo recebe 0 argumentos genéricos), dois E0308 de tipos incompatíveis e um E0277. Não vá atrás deles. O Rust ficou com o primeiro import, então todo Result no arquivo agora significa fmt::Result, e parse_temperature deixa de fazer sentido. Corrija o E0252 e todos eles somem.

O rustc aponta para duas linhas use:

  1. A linha --- (linha 1): “previous import of the type Result here” (import anterior do tipo Result aqui). Esta veio primeiro, então é a que o Rust mantém.
  2. A linha ^^^ (linha 2): “Result reimported here” (Result importado de novo aqui). O segundo import tenta ocupar o mesmo nome.
  3. O help: sugere renomear o segundo import com as.

std::fmt::Result e std::io::Result são mesmo diferentes. fmt::Result é um simples Result<(), fmt::Error>, usado por Display e Debug. io::Result<T> é Result<T, io::Error>, usado por arquivos e pela rede. Mesmo nome, trabalhos diferentes.

É como salvar dois contatos como “Mãe” no celular: a sua mãe e a sua sogra. Quando você toca em “Mãe”, o celular precisa saber para qual das duas ligar. Você acrescenta uma palavra a um deles, ou salva como “Mãe (Ana)” e “Mãe (Lia)”.

Dentro de um módulo, cada nome aponta para exatamente um item. Uma linha use não copia nada; ela acrescenta um nome curto para um item que mora em outro lugar. Duas linhas use que acrescentam o mesmo nome curto deixariam ambíguo todo Result que viesse depois, então o Rust para no import em vez de tentar adivinhar em cada uso.

O E0252 é um de três irmãos com a mesma primeira linha: o E0255 é uma linha use contra um item seu, e o E0428 são dois itens seus. A regra por trás dos três é “um nome, uma coisa, por módulo”. Veja Módulos.

Correção 1: Importe os módulos e escreva fmt:: e io::

Seção intitulada “Correção 1: Importe os módulos e escreva fmt:: e io::”

Escolha esta na maioria das vezes, e sempre para Result e Error. Importe os módulos pai e escreva o nome do módulo na frente. Agora cada uso diz qual Result ele é. O Rust Book recomenda esse estilo exatamente 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("não é um número")),
}
}
fn main() {
for input in ["21", " -4 ", "quente"] {
match parse_temperature(input) {
Ok(t) => println!("{}", t),
Err(e) => println!("erro: {}", e),
}
}
}

Saída:

21 °C
-4 °C
erro: não é um número
▶ Experimente você mesmo

Bônus: o Result<T, E> comum do prelude continua livre para usar, sem nenhum import.

Escolha esta quando você usa muito um dos dois tipos e quer um nome curto para ele. Esta é a sugestão do rustc. Escolha um nome que diga de onde ele vem, como IoResult, em vez 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("não é um número")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("erro: {}", e),
}
}

Saída:

21 °C
▶ Experimente você mesmo

Um Result simples que na verdade significa fmt::Result pode surpreender quem ler o código depois, então a Correção 1 costuma ser mais clara. O as brilha com nomes longos, como use std::collections::BTreeMap as Map;.

Escolha esta para traits que você só importa para chamar os métodos deles. Os dois traits Write são o caso clássico: o fmt::Write deixa você usar write! numa String, e o io::Write deixa você usar write! no stdout ou num arquivo. Importar os dois pelo nome dá conflito:

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; // ❌ um segundo `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; // ❌ um segundo `Write`
| +++++++++++++

É a mesma mensagem de antes, agora para o trait Write: o primeiro import, o segundo e a sugestão de renomear com as.

O programa com erro:

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

Aqui você nunca escreve Write; você só precisa dos métodos dele. O as _ importa um trait sem dar um nome a ele, então nada pode entrar em conflito, e os dois conjuntos de métodos continuam funcionando:

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

Saída:

12 testes passaram
▶ Experimente você mesmo

Se você precisa dar nome a um deles, por exemplo em fn log(w: &mut impl io::Write), combine isso com a Correção 1: use std::io; e escreva io::Write. Veja Traits.

Correção 4: Tire um import repetido do mesmo item

Seção intitulada “Correção 4: Tire um import repetido do mesmo item”

Escolha esta quando as duas linhas importam exatamente a mesma coisa, o que acontece muito depois de juntar código ou quando o seu editor acrescenta um import que você já tinha:

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` de novo
| ^^^^^^^--
| |
| `HashMap` reimported here
| help: remove unnecessary import

O rustc mostra o primeiro import de HashMap, o segundo e a ajuda “remove unnecessary import” (tire o import desnecessário).

O programa com erro:

use std::collections::HashMap;
use std::collections::{HashMap, HashSet}; // ❌ `HashMap` de novo
fn main() {
let mut stock: HashMap<&str, u32> = HashMap::new();
stock.insert("maçãs", 3);
let mut sold_out: HashSet<&str> = HashSet::new();
sold_out.insert("peras");
println!("maçãs: {}, esgotados: {}", stock["maçãs"], sold_out.len());
}

Repare que a ajuda é diferente: nada de as, só “remove unnecessary import”. Fique com uma linha só:

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

Saída:

maçãs: 3, esgotados: 1
▶ Experimente você mesmo

Veja HashMaps e HashSet.

Error vindo de dois lugares. use std::fmt::Error; e use std::io::Error;, ou use std::error::Error; junto com use anyhow::Error;. Correção: use std::io; e escreva io::Error, ou use anyhow::Error as AnyError;. Veja anyhow e thiserror.

Dois crates com o mesmo nome de tipo. use chrono::Duration; e use std::time::Duration; são uma dupla comum em projetos que lidam com datas. Correção: deixe um com o nome curto e escreva o módulo do outro, como chrono::Duration. Veja Datas e horas.

Imports glob. use std::fmt::*; mais use std::io::*; não dá E0252 nas linhas use. O problema aparece depois, no primeiro Result, como um erro sem código: error: `Result` is ambiguous (Result é ambíguo). Correção: importe explicitamente os nomes de que você precisa, como na Correção 1.

  • E0255: uma linha use se choca com uma struct, enum ou função sua de mesmo nome.
  • E0428: dois itens seus têm o mesmo nome, muitas vezes por tentar sobrecarregar uma função.
  • E0432: import não resolvido, quando a linha use aponta para algo que não existe.
  • E0034: dois traits em escopo dão a um tipo métodos com o mesmo nome.
  • E0308: tipos incompatíveis, um dos erros que aparecem em seguida depois de um conflito de Result.
  • Todos os erros do compilador: a lista completa de páginas de erro.

Teste o que aprendeu

1. Você precisa de fmt::Result e io::Result no mesmo arquivo. Quais imports são a escolha limpa de costume?

2. Você precisa dos métodos de std::fmt::Write e de std::io::Write, mas nunca escreve o nome dos traits. O que funciona?

3. O rustc diz que HashMap is reimported here e sugere remove unnecessary import. O que aconteceu?

Pontuação: 0 / 3