Lewati ke konten

Error Rust E0255: the name is defined multiple times (import vs item lokal)

E0255 artinya: sebuah baris use dan sesuatu yang kamu definisikan sendiri punya nama yang sama. Contoh klasiknya: kamu meng-import std::io::Error di bagian atas file, lalu di bawahnya menulis enum Error buatanmu sendiri. Rust tidak bisa membiarkan satu nama berarti dua hal, jadi ia berhenti. Error ini sangat sering muncul saat kamu mulai menulis tipe error sendiri, dan perbaikannya cukup satu baris pendek. Berbeda dengan E0412, kode ini tidak berubah di rilis-rilis terbaru: rustc 1.97 masih melaporkannya sebagai E0255. (Halaman ini bagian dari Penjelasan error compiler 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` here
2 |
3 | enum Error { // ❌ nama sama dengan import di baris 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;
| ^^^^^^^^^^^^^^

Ini program yang menghasilkan error tersebut:

use std::io::Error;
enum Error { // ❌ nama sama dengan import di baris 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!("nama sudah benar"),
Err(Error::EmptyName) => println!("nama kosong"),
Err(Error::TooLong(n)) => println!("nama punya {} huruf, maksimal 10", n),
}
}

rustc menunjuk ke tiga hal:

  1. Baris --- (baris 1): “previous import of the type Error here” (import sebelumnya untuk tipe Error ada di sini). Baris use itu sudah memasukkan nama Error ke modul ini.
  2. Baris ^^^ (baris 3): “Error redefined here” (Error didefinisikan ulang di sini). enum Error buatanmu mencoba memakai nama yang sama.
  3. help:-nya menawarkan jalan keluar: beri import itu nama lokal yang lain dengan as.

warning: unused import (import tidak dipakai) di bawahnya adalah petunjuk yang berguna. Artinya, tidak ada bagian kodemu yang benar-benar memakai std::io::Error (setiap Error di program ini maksudnya enum buatanmu). Kalau begitu, kamu cukup menghapus baris 1. Kalau kamu memang butuh keduanya, baca terus.

Catatannya menyebut type namespace (namespace tipe). Tipe, trait, dan modul berbagi satu daftar nama; fungsi, konstanta, dan static berbagi daftar lain. Jadi use std::mem::swap; ditambah fn swap buatanmu sendiri adalah error yang sama, tapi di “value namespace” (namespace nilai).

Bayangkan sebuah kelas dengan dua murid bernama Andi. Guru tidak bisa lagi sekadar memanggil “Andi”. Salah satunya butuh nama panggilan, seperti “Andi B.”, dan itulah persisnya yang diberikan as kepada sebuah import.

Sebuah nama di dalam modul menunjuk ke tepat satu item. Saat Rust membaca Error::EmptyName di baris 10, ia harus tahu Error mana yang kamu maksud, dan ia menolak menebak. Kalau diam-diam memilih enum-mu, baris use-mu jadi tidak berguna; kalau memilih io::Error, enum-mu tidak bisa dipakai. Apa pun pilihannya, pilihan diam-diam akan menyembunyikan sebuah kesalahan.

Karena itulah E0255 bersaudara dengan E0428 (dua item buatanmu sendiri dengan satu nama) dan E0252 (dua baris use dengan satu nama). Aturannya sama: satu nama, satu hal, per modul. Lihat Modul untuk cara kerja use.

Pilih ini kalau kamu butuh kedua tipe di file yang sama, dan itu cukup sering: Error buatanmu untuk aturan aplikasimu, dan io::Error untuk file dan jaringan. Beri import itu nama panggilan yang menunjukkan asalnya. IoError lebih enak dibaca daripada nama sementara dari rustc, OtherError:

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, "nama itu sudah dicadangkan"));
}
Ok(())
}
fn main() {
println!("{:?}", check_name("Ferris"));
println!("{:?}", check_name(""));
println!("{:?}", check_name("Ferris the Crab"));
match save_name("root") {
Ok(()) => println!("tersimpan"),
Err(e) => println!("error io: {}", e),
}
}

Output:

Ok(())
Err(EmptyName)
Err(TooLong(15))
error io: nama itu sudah dicadangkan
▶ Coba sendiri

as hanya mengganti nama di file ini. Di tempat lain, tipenya tetap std::io::Error.

Perbaikan 2: Import modulnya, bukan tipenya

Section titled “Perbaikan 2: Import modulnya, bukan tipenya”

Pilih ini kalau kamu ingin setiap Error menunjukkan asalnya di setiap tempat ia dipakai. Import modul induknya dengan use std::io; lalu tulis io::Error. Error buatanmu tetap memakai nama pendeknya. Banyak programmer Rust lebih suka gaya ini untuk tipe dengan nama umum seperti Error dan 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, "nama yang dicadangkan"));
}
Ok(())
}
fn main() {
if let Err(Error::EmptyName) = check_name("") {
println!("nama kosong");
}
if let Err(e) = save_name("root") {
println!("error io: {}", e);
}
}

Output:

nama kosong
error io: nama yang dicadangkan
▶ Coba sendiri

Perbaikan 3: Ganti nama item buatanmu sendiri

Section titled “Perbaikan 3: Ganti nama item buatanmu sendiri”

Pilih ini kalau tipemu akan dipakai di banyak file, atau oleh orang lain. Nama yang spesifik seperti NameError atau AppError lebih jelas daripada Error kedua, dan baris use-nya bisa tetap seperti semula:

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("disk penuh");
println!("{}", disk);
}

Output:

Err(TooLong(15))
disk penuh
▶ Coba sendiri

Untuk proyek yang lebih besar, crate thiserror membuat enum error seperti ini jauh lebih singkat untuk ditulis. Lihat Error Kustom.

Perbaikan 4: Hapus import yang tidak kamu pakai

Section titled “Perbaikan 4: Hapus import yang tidak kamu pakai”

Pilih ini kalau rustc juga menampilkan warning: unused import, seperti pada error di atas. Import itu sisa dari kode lama, atau ditambahkan oleh editor-mu. Hapus barisnya, dan program di awal halaman ini langsung jalan seperti yang ditulis:

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!("nama sudah benar"),
Err(Error::EmptyName) => println!("nama kosong"),
Err(Error::TooLong(n)) => println!("nama punya {} huruf, maksimal 10", n),
}
}

Output:

nama kosong
▶ Coba sendiri

Fungsi buatanmu dengan nama dari std. use std::mem::swap; ditambah fn swap(a: &mut i32, b: &mut i32) memberi E0255 dengan “previous import of the value swap here” dan catatan “value namespace”. Perbaikannya: beri nama swap_values pada fungsimu, atau hapus import-nya kalau kamu memang ingin menulis versimu sendiri.

Copy-paste antarmodul. Kamu memindahkan struct User ke mod models, menambahkan use models::User; di bagian atas, tapi lupa menghapus struct User yang lama di main.rs. Perbaikannya: hapus salinan lamanya, supaya User hanya berarti yang ada di models.

Trait dengan nama dari std. use std::fmt::Display; dan trait Display buatanmu bentrok dengan cara yang sama (“previous import of the trait Display”). Perbaikannya: ganti nama trait-mu (Describe, Render), atau tulis use std::fmt; dan fmt::Display.

Perlu diketahui: import glob seperti use std::io::*; tidak pernah menyebabkan E0255. Item buatanmu sendiri diam-diam menang atas nama yang masuk lewat *. Itu praktis, tapi juga bisa menyembunyikan Error mana yang sedang kamu pakai, jadi import yang eksplisit biasanya lebih jelas.

  • E0252: dua baris use memasukkan nama yang sama, seperti fmt::Result dan io::Result.
  • E0428: dua item buatanmu sendiri bernama sama, sering karena mencoba overloading sebuah fungsi.
  • E0592: dua method dengan nama yang sama di blok impl.
  • E0432: baris use itu sendiri menunjuk ke sesuatu yang tidak ada.
  • E0425: masalah kebalikannya: nama yang sama sekali tidak didefinisikan di tempat kamu memakainya.
  • Semua error compiler: daftar lengkap halaman error.

Kuis singkat

1. File-mu diawali use std::fmt::Result; dan di bawahnya mendefinisikan enum Result { Pass, Fail }. Error apa yang kamu dapat?

2. Kamu butuh std::io::Error dan enum Error buatanmu sendiri di satu file. Apa perbaikan yang rapi?

3. rustc menampilkan E0255 dan juga warning: unused import: std::io::Error. Apa yang diberitahukan warning itu?

Skor: 0 / 3