Перейти к содержимому

Ошибка Rust E0252: the name is defined multiple times (два импорта)

E0252 значит: две строки use приносят одно и то же имя. Хрестоматийный случай — use std::fmt::Result; вместе с use std::io::Result;: два разных типа, и оба называются Result. Бывает ещё fmt::Error и io::Error или два трейта Write. Ошибка очень частая и исправляется легко — обычно достаточно поменять одну-две строки use. rustc 1.97 по-прежнему сообщает её под собственным кодом, E0252. Страница входит в раздел Ошибки компилятора Rust с объяснениями.

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; // ❌ a second `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; // ❌ a second `Result`
| ++++++++++++++

По-русски: «имя Result определено несколько раз». Первый импорт отмечен как «предыдущий», второй — как «импортирован повторно», а help предлагает переименовать второй через as.

Вот программа, которая её вызывает (комментарий // ❌ оставлен на английском, потому что rustc цитирует эту строку; он значит «второй Result»):

use std::fmt::Result;
use std::io::Result; // ❌ a second `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("not a number")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("error: {}", e),
}
}

После неё rustc выводит ещё четыре ошибки: E0107 («type alias takes 0 generic arguments»), две E0308 о несовпадении типов и одну E0277. Не гоняйся за ними. Rust оставил первый импорт, поэтому каждый Result в файле теперь значит fmt::Result, и parse_temperature теряет смысл. Исправь E0252 — и все они исчезнут.

rustc указывает на две строки use:

  1. Строка с --- (строка 1): «previous import of the type Result here» — здесь был предыдущий импорт типа Result. Он был первым, поэтому Rust оставляет именно его.
  2. Строка с ^^^ (строка 2): «Result reimported here» — здесь Result импортирован повторно. Второй импорт пытается занять то же имя.
  3. help: предлагает переименовать второй импорт через as.

std::fmt::Result и std::io::Result действительно разные. fmt::Result — это просто Result<(), fmt::Error>, его используют Display и Debug. io::Result<T> — это Result<T, io::Error>, его используют файлы и сеть. Имя одно, а работа разная.

Это как сохранить в телефоне два контакта «Мама»: твою маму и маму твоего партнёра. Когда ты нажимаешь «Мама», телефон должен знать, кому звонить. Ты добавляешь слово к одному из них или сохраняешь их как «Мама (Аня)» и «Мама (Лида)».

Внутри одного модуля каждое имя указывает ровно на один элемент. Строка use ничего не копирует, она добавляет короткое имя для элемента, который живёт в другом месте. Две строки use, добавляющие одно и то же короткое имя, сделали бы неоднозначным каждый следующий Result, поэтому Rust останавливается на импорте, а не гадает при каждом использовании.

У E0252 две «сестры» с тем же заголовком: E0255 — строка use против твоего собственного элемента, и E0428 — два твоих собственных элемента. За всеми тремя стоит одно правило: в модуле одно имя — одна вещь. Подробнее — в уроке Модули.

Исправление 1: Импортируй модули и пиши fmt:: и io::

Заголовок раздела «Исправление 1: Импортируй модули и пиши fmt:: и io::»

Выбирай этот вариант в большинстве случаев, а для Result и Error — всегда. Импортируй родительские модули и пиши имя модуля впереди. Теперь каждое использование говорит, какой это Result. Книга по Rust советует именно такой стиль для этого случая:

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("это не число")),
}
}
fn main() {
for input in ["21", " -4 ", "тепло"] {
match parse_temperature(input) {
Ok(t) => println!("{}", t),
Err(e) => println!("ошибка: {}", e),
}
}
}

Вывод:

21 °C
-4 °C
ошибка: это не число
▶ Попробуй сам

Бонус: обычный Result<T, E> из прелюдии (prelude) по-прежнему доступен без всякого импорта.

Исправление 2: Переименуй один импорт через as

Заголовок раздела «Исправление 2: Переименуй один импорт через as»

Выбирай этот вариант, когда один из двух типов ты используешь часто и хочешь для него короткое имя. Это и есть подсказка rustc. Выбери имя, по которому видно, откуда тип, например IoResult, а не 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("это не число")),
}
}
fn main() {
match parse_temperature("21") {
Ok(t) => println!("{}", t),
Err(e) => println!("ошибка: {}", e),
}
}

Вывод:

21 °C
▶ Попробуй сам

Простой Result, который втайне значит fmt::Result, может удивить следующего читателя кода, поэтому исправление 1 обычно понятнее. as хорош для длинных имён, например use std::collections::BTreeMap as Map;.

Выбирай этот вариант для трейтов, которые ты импортируешь только ради их методов. Классика — два трейта Write: fmt::Write позволяет писать через write! в String, а io::Write — в stdout или в файл. Если импортировать оба по имени, они конфликтуют:

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; // ❌ a second `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; // ❌ a second `Write`
| +++++++++++++

Это та же ошибка, только для трейта. Программа с ошибкой:

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

Здесь ты нигде не пишешь Write сам, тебе нужны только его методы. as _ импортирует трейт без имени, поэтому конфликтовать нечему, а оба набора методов продолжают работать:

use std::fmt::Write as _;
use std::io::Write as _;
fn main() {
let mut report = String::new();
write!(report, "тестов пройдено: {}", 12).unwrap();
let mut out = std::io::stdout();
writeln!(out, "{}", report).unwrap();
}

Вывод:

тестов пройдено: 12
▶ Попробуй сам

Если одному из них всё же нужно имя, например в fn log(w: &mut impl io::Write), совмести это с исправлением 1: use std::io; и пиши io::Write. Подробнее — в уроке Трейты.

Исправление 4: Убери повторный импорт того же элемента

Заголовок раздела «Исправление 4: Убери повторный импорт того же элемента»

Выбирай этот вариант, когда обе строки импортируют одно и то же. Так часто бывает после слияния кода или когда редактор добавляет импорт, который уже был:

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

Программа с ошибкой (комментарий значит «снова HashMap»):

use std::collections::HashMap;
use std::collections::{HashMap, HashSet}; // ❌ `HashMap` again
fn main() {
let mut stock: HashMap<&str, u32> = HashMap::new();
stock.insert("apples", 3);
let mut sold_out: HashSet<&str> = HashSet::new();
sold_out.insert("pears");
println!("apples: {}, sold out: {}", stock["apples"], sold_out.len());
}

Обрати внимание, что подсказка другая: никакого as, только «remove unnecessary import» («удали лишний импорт»). Оставь одну строку:

use std::collections::{HashMap, HashSet};
fn main() {
let mut stock: HashMap<&str, u32> = HashMap::new();
stock.insert("яблоки", 3);
let mut sold_out: HashSet<&str> = HashSet::new();
sold_out.insert("груши");
println!("яблоки: {}, распродано: {}", stock["яблоки"], sold_out.len());
}

Вывод:

яблоки: 3, распродано: 1
▶ Попробуй сам

Подробнее — в уроках HashMap и HashSet.

Error из двух мест. use std::fmt::Error; и use std::io::Error; или use std::error::Error; рядом с use anyhow::Error;. Исправление: use std::io; и io::Error в коде или use anyhow::Error as AnyError;. Подробнее — в уроке anyhow и thiserror.

Два крейта с одинаковым именем типа. use chrono::Duration; и use std::time::Duration; — частая пара в проектах, которые работают с датами. Исправление: оставь короткое имя для одного, а у другого пиши модуль, например chrono::Duration. Подробнее — в уроке Дата и время.

Импорты со звёздочкой. use std::fmt::*; вместе с use std::io::*; не дают E0252 на строках use. Проблема появится позже, на первом Result, в виде ошибки без кода: error: `Result` is ambiguous («Result неоднозначен»). Исправление: импортируй нужные имена явно, как в исправлении 1.

  • E0255: строка use конфликтует с твоей собственной структурой, перечислением или функцией с тем же именем.
  • E0428: два твоих собственных элемента с одним именем, часто из-за попытки перегрузить функцию.
  • E0432: неразрешённый импорт, когда строка use указывает на то, чего не существует.
  • E0034: два трейта в области видимости дают типу методы с одинаковым именем.
  • E0308: несовпадение типов, одна из ошибок-следствий после конфликта Result.
  • Все ошибки компилятора: полный список страниц об ошибках.

Проверь себя

1. В одном файле нужны fmt::Result и io::Result. Какие импорты обычно выбирают как чистое решение?

2. Тебе нужны методы и std::fmt::Write, и std::io::Write, но имена трейтов ты сам не пишешь. Что сработает?

3. rustc пишет, что HashMap импортирован повторно, и предлагает remove unnecessary import. Что случилось?

Результат: 0 / 3