Ошибка Rust E0255: the name is defined multiple times (импорт и свой элемент)
E0255 значит: строка use и что-то, что ты определил сам, называются одинаково. Классический случай: в начале файла ты импортируешь std::io::Error, а ниже пишешь собственный enum Error. Rust не может разрешить одному имени значить две вещи, поэтому останавливается. Это очень частая ошибка, когда начинаешь писать свои типы ошибок, а исправляется она одной короткой строкой. В отличие от E0412, этот код в последних версиях не менялся: rustc 1.97 по-прежнему сообщает E0255. Страница входит в раздел Ошибки компилятора Rust с объяснениями.
Сообщение об ошибке
Заголовок раздела «Сообщение об ошибке»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 { // ❌ same name as the import on line 1 | ^^^^^^^^^^ `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; | +++++++++++++
warning: unused import: `std::io::Error` --> src/main.rs:1:5 |1 | use std::io::Error; | ^^^^^^^^^^^^^^По-русски: «имя Error определено несколько раз». Ниже rustc предлагает переименовать импорт через as и предупреждает, что импорт не используется.
Вот программа, которая её вызывает (комментарий // ❌ оставлен на английском, потому что rustc цитирует эту строку; он значит «то же имя, что у импорта в строке 1»):
use std::io::Error;
enum Error { // ❌ same name as the import on line 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!("name is fine"), Err(Error::EmptyName) => println!("name is empty"), Err(Error::TooLong(n)) => println!("name has {} letters, max is 10", n), }}Что это значит
Заголовок раздела «Что это значит»rustc указывает на три вещи:
- Строка с
---(строка 1): «previous import of the typeErrorhere» — здесь уже был импорт типаError. Строкаuseуже положила имяErrorв этот модуль. - Строка с
^^^(строка 3): «Errorredefined here» — здесьErrorопределяется заново. Твойenum Errorпытается занять то же имя. help:предлагает выход: дать импорту другое локальное имя черезas.
warning: unused import под ошибкой — полезная подсказка. Она значит, что в твоём коде std::io::Error на самом деле нигде не используется (каждый Error в этой программе — это твоё перечисление). Тогда строку 1 можно просто удалить. Если тебе нужны оба типа, читай дальше.
В заметке (note) упоминается пространство имён типов (type namespace). Типы, трейты и модули делят один список имён, а функции, константы и статические переменные — другой. Поэтому use std::mem::swap; вместе с твоей fn swap — та же ошибка, только в «пространстве имён значений» (value namespace).
Представь класс, где учатся два Саши. Учитель уже не может просто позвать «Саша!». Кому-то нужно прозвище, например «Саша Б.», — и именно его as даёт импорту.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»Имя в модуле указывает ровно на один элемент. Когда Rust читает Error::EmptyName в строке 10, ему нужно знать, какой Error ты имеешь в виду, и угадывать он отказывается. Если бы он тихо выбрал твоё перечисление, строка use ничего бы не делала; если бы выбрал io::Error, до твоего перечисления нельзя было бы добраться. В любом случае молчаливый выбор спрятал бы ошибку.
Поэтому E0255 — родственница E0428 (два твоих собственных элемента с одним именем) и E0252 (две строки use с одним именем). Правило одно: в модуле одно имя — одна вещь. Как работает use, рассказано в уроке Модули.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: Переименуй импорт через as
Заголовок раздела «Исправление 1: Переименуй импорт через as»Выбирай этот вариант, когда в одном файле нужны оба типа — а так бывает часто: твой Error для правил приложения и io::Error для файлов и сети. Дай импорту прозвище, по которому видно, откуда он. IoError читается лучше, чем OtherError из подсказки 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, "это имя зарезервировано")); } Ok(())}
fn main() { println!("{:?}", check_name("Ferris")); println!("{:?}", check_name("")); println!("{:?}", check_name("Ferris the Crab")); match save_name("root") { Ok(()) => println!("сохранено"), Err(e) => println!("ошибка ввода-вывода: {}", e), }}Вывод:
Ok(())Err(EmptyName)Err(TooLong(15))ошибка ввода-вывода: это имя зарезервированоИмена вроде "Ferris the Crab" оставлены на английском, потому что программа считает их длину через .len(), а кириллическая буква занимает 2 байта.
as меняет имя только в этом файле. Везде остальном тип по-прежнему называется std::io::Error.
Исправление 2: Импортируй модуль, а не тип
Заголовок раздела «Исправление 2: Импортируй модуль, а не тип»Выбирай этот вариант, если хочешь, чтобы при каждом использовании Error было видно, откуда он. Импортируй родительский модуль через use std::io; и пиши io::Error. Твой собственный Error сохраняет короткое имя. Многие программисты на Rust предпочитают такой стиль для типов с частыми именами вроде Error и 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, "зарезервированное имя")); } Ok(())}
fn main() { if let Err(Error::EmptyName) = check_name("") { println!("имя пустое"); } if let Err(e) = save_name("root") { println!("ошибка ввода-вывода: {}", e); }}Вывод:
имя пустоеошибка ввода-вывода: зарезервированное имяИсправление 3: Переименуй свой элемент
Заголовок раздела «Исправление 3: Переименуй свой элемент»Выбирай этот вариант, когда твой тип будет использоваться во многих файлах или другими людьми. Конкретное имя вроде NameError или AppError понятнее, чем второй Error, а строку use можно оставить как есть:
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("диск заполнен"); println!("{}", disk);}Вывод:
Err(TooLong(15))диск заполненВ больших проектах крейт thiserror заметно сокращает такие перечисления ошибок. Подробнее — в уроке Свои типы ошибок.
Исправление 4: Удали импорт, который не используется
Заголовок раздела «Исправление 4: Удали импорт, который не используется»Выбирай этот вариант, когда rustc также пишет warning: unused import («неиспользуемый импорт»), как в ошибке выше. Импорт остался от старого кода или его добавил редактор. Удали строку — и программа из начала страницы заработает как есть:
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!("с именем всё в порядке"), Err(Error::EmptyName) => println!("имя пустое"), Err(Error::TooLong(n)) => println!("в имени {} байт, а можно не больше 10", n), }}Вывод:
имя пустоеГде это обычно встречается
Заголовок раздела «Где это обычно встречается»Твоя функция с именем из std. use std::mem::swap; вместе с fn swap(a: &mut i32, b: &mut i32) даёт E0255 с текстом «previous import of the value swap here» и заметкой про «value namespace». Исправление: назови свою функцию swap_values или убери импорт, если хотел написать свою.
Копирование между модулями. Ты переносишь struct User в mod models, добавляешь сверху use models::User;, но забываешь удалить старую struct User в main.rs. Исправление: удали старую копию, чтобы User означал только тот, что в models.
Трейт с именем из std. use std::fmt::Display; и твой собственный trait Display конфликтуют так же («previous import of the trait Display»). Исправление: переименуй свой трейт (Describe, Render) или пиши use std::fmt; и fmt::Display.
Полезно знать: импорт со звёздочкой (glob), например use std::io::*;, никогда не вызывает E0255. Твои собственные элементы тихо побеждают имена, пришедшие через *. Это удобно, но может скрыть, какой именно Error ты используешь, поэтому явные импорты обычно понятнее.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0252: две строки
useприносят одно и то же имя, напримерfmt::Resultиio::Result. - E0428: два твоих собственных элемента с одним именем, часто из-за попытки перегрузить функцию.
- E0592: два метода с одним именем в блоке
impl. - E0432: сама строка
useуказывает на то, чего не существует. - E0425: обратная проблема — имя вообще не определено там, где ты его используешь.
- Все ошибки компилятора: полный список страниц об ошибках.
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Файл начинается с use std::fmt::Result;, а ниже определён enum Result { Pass, Fail }. Какую ошибку ты получишь?
2. В одном файле нужны и std::io::Error, и твой enum Error. Какое исправление будет чистым?
3. rustc показывает E0255 и ещё warning: unused import: std::io::Error. О чём говорит предупреждение?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- E0255 — Rust error codes index (на английском): официальное объяснение, где
use bar::foo;конфликтует сfn foo, с исправлениями черезasили родительский путь. - Предоставление новых имён с помощью ключевого слова as — книга «Язык программирования Rust» (перевод на русский): как
asпереименовывает импорт, на примере двух типовResult. - Use declarations — The Rust Reference (на английском): все формы
use, включаяas, группы и звёздочку. - Namespaces — The Rust Reference (на английском): пространства имён типов и значений из заметки к ошибке.
- Декларация use — Rust на примерах (перевод на русский): короткие примеры импорта и переименования, которые можно запустить.