Lewati ke konten

Error Rust E0609: no field `x` on type `T` (field tidak ada)

E0609 artinya: kamu menulis value.something, tapi tipe value tidak punya field bernama something. Biasanya ini salah ketik, atau nilainya bukan tipe yang kamu kira (misalnya Option<User>, bukan User). Pesan error-nya menyebutkan tipe persis yang dilihat Rust, dan biasanya itu saja sudah cukup untuk memperbaikinya. (Halaman ini bagian dari Penjelasan error compiler Rust.)

error[E0609]: no field `emial` on type `User`
--> src/main.rs:12:25
|
12 | println!("{}", user.emial); // ❌ salah ketik: nama field-nya `email`
| ^^^^^ unknown field
|
help: a field with a similar name exists
|
12 - println!("{}", user.emial); // ❌ salah ketik: nama field-nya `email`
12 + println!("{}", user.email); // ❌ salah ketik: nama field-nya `email`
|

Compiler bilang tipe User tidak punya field emial, dan memberi tahu bahwa ada field dengan nama yang mirip: email.

Ini program yang menghasilkan error tersebut:

struct User {
name: String,
email: String,
}
fn main() {
let user = User {
name: String::from("Ferris"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.emial); // ❌ salah ketik: nama field-nya `email`
}

Judul pesannya punya dua bagian, dan keduanya penting:

  • no field `emial` (tidak ada field emial): nama setelah titik yang tidak ditemukan Rust.
  • on type `User` (pada tipe User): tipe yang menurut Rust dimiliki user.

Jadi, cek namanya dulu, lalu cek tipenya. Kalau tipe di pesan itu bukan struct yang kamu harapkan, misalnya Option<User>, &Option<User>, Vec<User> atau &T, berarti namanya sudah benar, dan masalahnya adalah kamu perlu mengeluarkan struct-nya dulu.

Ibaratnya kamu menanyakan “yang biru di rak kedua” padahal kamu sedang berdiri di toko yang salah. Permintaannya tidak salah, kamu saja yang ada di tempat yang keliru.

Di Rust, field setiap struct sudah tetap sejak struct itu ditulis. Compiler tahu daftar lengkapnya dan memeriksa setiap .field saat kompilasi. Tidak ada “cari saat runtime lalu dapat undefined” seperti di JavaScript, atau AttributeError seperti di Python: nama field yang salah tidak akan pernah masuk ke programmu. Lihat Struct.

Tipe pembungkus seperti Option<T> dan Result<T, E> adalah tipe tersendiri: struct-mu ada di dalamnya, tapi tipe pembungkus itu tidak punya field-field struct-mu. Rust tidak menembusnya untukmu, karena bisa saja nilainya sama sekali tidak ada. Kamu harus menentukan apa yang terjadi kalau nilainya kosong. Lihat Option.

Kode generic mengikuti aturan ketat yang sama. Di fn greet<T>(x: &T), T bisa tipe apa saja, termasuk i32 atau bool yang tidak punya field. Rust memeriksa fungsi generic satu kali untuk semua kemungkinan T, jadi ia hanya mengizinkanmu memakai apa yang dijanjikan oleh trait bound-mu. Dan trait hanya bisa menjanjikan method, bukan field. Lihat Generik dan Trait.

Pilih ini kalau tipe di pesan error adalah struct yang kamu harapkan. Cari baris help: a field with a similar name exists (ada field dengan nama yang mirip); rustc biasanya tahu persis field mana yang kamu maksud:

struct User {
name: String,
email: String,
}
fn main() {
let user = User {
name: String::from("Ferris"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.email); // ✅ nama field yang sebenarnya
}

Output:

▶ Coba sendiri

Kalau tidak ada petunjuk, lompat ke definisi struct-nya (di kebanyakan editor: Ctrl+klik atau F12 pada tipenya) dan salin namanya dari sana. Nama field membedakan huruf besar dan kecil: userName dan user_name adalah field yang berbeda.

Perbaikan 2: Keluarkan struct-nya dari Option dulu

Section titled “Perbaikan 2: Keluarkan struct-nya dari Option dulu”

Pilih ini kalau pesannya bilang tipenya Option<User>, Option<&User> atau &Option<User>. Ini sering sekali terjadi dengan fungsi yang mungkin tidak menemukan sesuatu, dan dengan method seperti .first(), .last(), .get() dan .find():

error[E0609]: no field `name` on type `Option<User>`
--> src/main.rs:11:25
|
11 | println!("{}", user.name);
| ^^^^ unknown field
|
help: one of the expressions' fields has a field of the same name
|
11 | println!("{}", user.unwrap().name);
| +++++++++

Di sini rustc menyarankan .unwrap(), karena nilai di dalam Option punya field dengan nama yang sama. Saran itu memang bisa dikompilasi, tapi programnya akan crash kalau nilainya None. Lebih baik: tentukan apa yang terjadi di kedua kasus, dengan if let, match, atau map ditambah nilai default:

struct User {
name: String,
}
fn find_user(id: u32) -> Option<User> {
if id == 1 {
Some(User { name: String::from("Ferris") })
} else {
None
}
}
fn main() {
// if let: jalankan kodenya hanya kalau ada User
if let Some(user) = find_user(1) {
println!("Ketemu: {}", user.name);
}
// match: tangani kedua kasus
match find_user(2) {
Some(user) => println!("Ketemu: {}", user.name),
None => println!("Tidak ada user dengan id 2"),
}
// map + unwrap_or: ambil sebuah field, atau pakai nilai default
let users = vec![User { name: String::from("Ada") }];
let first_name = users.first().map(|u| u.name.as_str()).unwrap_or("tidak ada");
println!("User pertama: {first_name}");
}

Output:

Ketemu: Ferris
Tidak ada user dengan id 2
User pertama: Ada
▶ Coba sendiri

Pola yang sama juga berlaku untuk referensi ke Option. Kalau maybe_user adalah &Option<User>, tulis if let Some(user) = maybe_user, dan user menjadi &User. (Untuk &Option<User>, rustc sama sekali tidak memberi petunjuk, jadi ini layak diingat.) Di dalam fungsi yang mengembalikan Option, let user = find_user(1)?; juga bisa. Lihat Menangani Option dengan match dan if let.

Perbaikan 3: Pakai .0 dan .1 pada tuple struct

Section titled “Perbaikan 3: Pakai .0 dan .1 pada tuple struct”

Pilih ini kalau tipenya adalah tuple struct, seperti struct Point(i32, i32);. Field-nya tidak punya nama, hanya posisi, jadi p.x menghasilkan no field `x` on type `Point` (dengan petunjuk p.0). Pakai posisinya, atau bongkar ke variabel bernama. Kalau kamu ingin nama, jadikan struct dengan field bernama:

struct Point(i32, i32); // tuple struct: field-nya .0 dan .1
struct Size {
width: u32, // field bernama: pakai .width dan .height
height: u32,
}
fn main() {
let p = Point(3, 4);
println!("x = {}, y = {}", p.0, p.1);
let Point(x, y) = p; // atau bongkar ke variabel bernama
println!("jarak kuadrat = {}", x * x + y * y);
let screen = Size { width: 1920, height: 1080 };
println!("{}x{}", screen.width, screen.height);
}

Output:

x = 3, y = 4
jarak kuadrat = 25
1920x1080
▶ Coba sendiri

Tuple biasa bekerja dengan cara yang sama. pair.2 pada tuple dua elemen menghasilkan E0609 dengan catatan available fields are: `0`, `1` (field yang tersedia: 0, 1). Lihat Mengakses elemen berdasarkan posisi.

Perbaikan 4: Pakai method trait, bukan field, pada T generic

Section titled “Perbaikan 4: Pakai method trait, bukan field, pada T generic”

Pilih ini kalau pesannya bilang on type `&T` (atau T), dengan label type parameter 'T' declared here. Kode seperti ini gagal, meskipun setiap tipe yang kamu berikan punya field name:

fn greet<T>(animal: &T) {
println!("Halo, {}!", animal.name); // ❌ no field `name` on type `&T`
}

Trait tidak bisa mewajibkan field, tapi bisa mewajibkan method getter. Definisikan trait kecil, implementasikan untuk setiap tipe, lalu tambahkan sebagai bound pada T:

trait Named {
fn name(&self) -> &str;
}
struct Dog {
name: String,
}
struct Robot {
model: String,
}
impl Named for Dog {
fn name(&self) -> &str {
&self.name
}
}
impl Named for Robot {
fn name(&self) -> &str {
&self.model // nama field-nya bahkan tidak harus sama
}
}
fn greet<T: Named>(thing: &T) {
println!("Halo, {}!", thing.name()); // pemanggilan method, bukan field
}
fn main() {
greet(&Dog { name: String::from("Rex") });
greet(&Robot { model: String::from("R2-D2") });
}

Output:

Halo, Rex!
Halo, R2-D2!
▶ Coba sendiri

Kalau fungsinya hanya pernah bekerja dengan satu tipe, kamu sama sekali tidak butuh generic: tulis fn greet(animal: &Dog), dan animal.name langsung bisa dipakai. Lihat Trait sebagai parameter fungsi.

Method tanpa (). Menulis word.len atau cart.total (padahal total adalah method) mirip dengan E0609, tapi rustc memberi kode lain: E0615, “attempted to take value of method” (mencoba mengambil nilai dari method), dengan petunjuk “use parentheses to call the method” (pakai tanda kurung untuk memanggil method-nya). Pada Vec, scores.len menghasilkan E0616, “field len of struct Vec is private” (field len di Vec bersifat privat), karena Vec memang punya field tersembunyi dengan nama itu. Perbaikannya sama untuk keduanya: word.len(), scores.len(), cart.total(). Dan "hello".length adalah E0609 biasa pada &str: di Rust namanya len(). Lihat String.

Vec, bukan satu item. dogs.name pada Vec<Dog> menghasilkan no field `name` on type `Vec<Dog>`. Sebuah daftar tidak punya name, yang punya adalah setiap item-nya. Perbaikannya: ambil satu dengan dogs[0].name, atau lakukan perulangan dengan for dog in &dogs { println!("{}", dog.name); }. Lihat Vektor.

Result, bukan nilainya. load_config().port menghasilkan no field `port` on type `Result<Config, String>`, dengan petunjuk .unwrap() yang sama. Perbaikannya: let config = load_config()?; di dalam fungsi yang mengembalikan Result, atau pakai match. Lihat Operator ?.

Field pada angka. age.value pada u32 lagi-lagi kode yang berbeda: E0610, “u32 is a primitive type and therefore doesn’t have fields” (u32 adalah tipe primitif, jadi tidak punya field). Angka, bool dan char sama sekali tidak punya field; mungkin yang kamu maksud adalah variabel struct.

Kuis singkat

1. Error-nya bilang tidak ada field name pada tipe Option<User>. Apa artinya?

2. Bagaimana membaca field pertama struct Meters(f64); dari nilai m?

3. Di fn show<T>(x: &T), bagaimana caranya membaca nama dari setiap T?

Skor: 0 / 3