Error Rust E0005: refutable pattern in local binding (let yang bisa gagal)
E0005 artinya kamu menulis let dengan pola yang mungkin tidak cocok, misalnya let Some(x) = value; padahal value bisa saja None. Rust perlu tahu apa yang terjadi dalam kasus itu, dan let biasa tidak punya tempat untuk menyatakannya. Perbaikannya adalah memberi kasus “tidak cocok” itu jalan keluar: biasanya let ... else, if let, atau match. Error ini sangat umum saat kamu mulai bekerja dengan Option dan Result, dan memperbaikinya cuma butuh semenit. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.
Pesan error-nya
Section titled “Pesan error-nya”error[E0005]: refutable pattern in local binding --> src/main.rs:4:9 |4 | let Some(top) = best; // ❌ what if the list was empty? | ^^^^^^^^^ pattern `None` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html = note: the matched value is of type `Option<i32>`help: you might want to use `let...else` to handle the variant that isn't matched |4 | let Some(top) = best else { todo!() }; // ❌ what if the list was empty? | ++++++++++++++++Compiler bilang pola Some(top) tidak mencakup kasus None, padahal let membutuhkan pola yang selalu cocok. Ia menyarankan let...else untuk menangani kasus yang tidak cocok.
Ini program yang menghasilkan error tersebut:
fn main() { let scores = vec![72, 95, 88]; let best = scores.iter().max().copied(); let Some(top) = best; // ❌ what if the list was empty? println!("Top score: {top}");}Kamu akan melihat error yang sama dengan pola lain. Hanya bagian “not covered” (tidak tercakup) yang berubah:
3 | let Ok(age) = input.parse::<i32>(); | ^^^^^^^ pattern `Err(_)` not covered
3 | let [host, port] = parts.as_slice(); | ^^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _, ..]` not coveredApa artinya
Section titled “Apa artinya”Ada dua kata kunci di sini:
- Pola refutable (bisa gagal) adalah pola yang mungkin tidak cocok.
Some(top)gagal kalau nilainyaNone.Ok(age)gagal padaErr.[host, port]gagal kalau slice-nya tidak berisi tepat dua item. - Pola irrefutable (tidak bisa gagal) selalu cocok. Nama biasa seperti
let top = best;selalu berhasil, begitu juga tuple dan struct sepertilet (x, y) = point;.
Sebuah let harus selalu berhasil, karena baris berikutnya memakai variabel yang dibuatnya. Kalau best ternyata None, top akan berisi apa di baris berikutnya? Tidak ada jawabannya, jadi Rust menghentikanmu. Label “not covered” mendaftar persis kasus yang kamu lupakan: None, Err(_), atau untuk slice tadi, “kosong”, “satu item”, dan “tiga item atau lebih”.
Bayangkan loker paket. let Some(top) = best; itu seperti pergi begitu saja dengan yakin ada paket di dalamnya, tanpa membuka pintunya. Rust memaksamu memutuskan apa yang harus dilakukan kalau lokernya kosong.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”let, parameter fungsi, dan perulangan for semuanya membutuhkan pola yang tidak bisa gagal. match, if let, while let, dan let ... else menerima pola yang bisa gagal, karena masing-masing punya tempat untuk kasus lainnya. Ini aturan yang sama dengan E0004, di mana sebuah match harus mencakup setiap nilai. Di sini, “match”-nya adalah let dengan satu cabang saja.
Di banyak bahasa, membaca nilai yang tidak ada memberimu null, undefined, atau crash saat program berjalan. Di Rust, list kosong, angka yang salah ketik di kolom teks, atau pengaturan yang tidak ada adalah kasus normal yang kamu tangani di kode. Option dan Result membuatnya terlihat, dan E0005 memastikan kamu tidak melewatkannya secara tidak sengaja. Lihat Pola di let dan Option.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: let ... else untuk keluar lebih awal
Section titled “Perbaikan 1: let ... else untuk keluar lebih awal”Pilih ini kalau sisa fungsi membutuhkan nilainya, dan tanpa nilai itu tidak ada hal berguna yang bisa dilakukan. Inilah yang disarankan help: dari rustc. Kamu tetap memakai polamu dan menambahkan blok else. Blok itu harus keluar: return, break, continue, atau panic.
fn port_from(text: &str) -> u16 { // ambil angkanya, atau keluar dari fungsi lebih awal let Ok(port) = text.trim().parse::<u16>() else { println!("'{text}' bukan port yang valid, memakai 8080"); return 8080; }; port}
fn main() { let scores = vec![72, 95, 88]; let Some(top) = scores.iter().max() else { println!("Belum ada skor"); return; }; println!("Skor tertinggi: {top}");
println!("Port: {}", port_from("3000")); println!("Port: {}", port_from("abc"));}Output:
Skor tertinggi: 95Port: 3000'abc' bukan port yang valid, memakai 8080Port: 8080Jangan biarkan else { todo!() } dari rustc apa adanya. todo!() akan panic saat dijalankan, jadi ganti dengan jalan keluar yang sungguhan. Selengkapnya di let-else.
Perbaikan 2: match atau if let kalau kedua kasus melakukan sesuatu
Section titled “Perbaikan 2: match atau if let kalau kedua kasus melakukan sesuatu”Pilih ini kalau kasus “tidak cocok” punya pekerjaannya sendiri, seperti mencetak error-nya, atau kalau kodenya tetap berlanjut apa pun hasilnya. match membuatmu bisa menangani setiap kasus dan memakai nilai error-nya. Ini juga cara untuk membongkar slice saat kamu tidak tahu panjangnya. Pakai if let kalau hanya satu kasus yang penting:
fn main() { // match: tangani kedua hasil, dan pakai error-nya for input in ["42", "forty-two"] { match input.parse::<i32>() { Ok(age) => println!("Umur: {age}"), Err(e) => println!("Tidak bisa membaca '{input}': {e}"), } }
// slice bisa punya panjang berapa saja: match berdasarkan bentuknya for address in ["localhost:8080", "localhost"] { let parts: Vec<&str> = address.split(':').collect(); match parts.as_slice() { [host, port] => println!("host = {host}, port = {port}"), _ => println!("'{address}' seharusnya berbentuk host:port"), } }
// if let: lakukan sesuatu hanya kalau ada nilainya let nickname: Option<&str> = None; if let Some(name) = nickname { println!("Hai, {name}!"); } else { println!("Hai, orang asing!"); }}Output:
Umur: 42Tidak bisa membaca 'forty-two': invalid digit found in stringhost = localhost, port = 8080'localhost' seharusnya berbentuk host:portHai, orang asing!Teks invalid digit found in string adalah pesan error dari Rust sendiri, jadi tetap dalam bahasa Inggris. Cabang _ menangkap semua panjang lainnya. Tanpa cabang itu, match akan memberimu E0004 dengan daftar “not covered” yang sama. Lihat If let dan Match.
Perbaikan 3: Nilai default dengan unwrap_or, atau teruskan dengan ?
Section titled “Perbaikan 3: Nilai default dengan unwrap_or, atau teruskan dengan ?”Pilih ini kalau ada nilai default yang masuk akal, atau kalau pemanggil yang seharusnya mengurus masalahnya. unwrap_or(x) memberimu nilainya, atau x kalau tidak ada. ? bekerja di dalam fungsi yang mengembalikan Result (atau Option): ia mengembalikan error-nya ke pemanggil dan menghentikan fungsi itu.
use std::num::ParseIntError;
// ? meneruskan error ke pemanggil, bukan menanganinya di sinifn total_minutes(hours: &str, minutes: &str) -> Result<i32, ParseIntError> { let h = hours.parse::<i32>()?; let m = minutes.parse::<i32>()?; Ok(h * 60 + m)}
fn main() { // nilai default kalau tidak ada apa-apa let scores: Vec<i32> = Vec::new(); let top = scores.iter().max().copied().unwrap_or(0); println!("Skor tertinggi: {top}");
let volume = "loud".parse::<u8>().unwrap_or(50); println!("Volume: {volume}");
println!("{:?}", total_minutes("2", "30")); println!("{:?}", total_minutes("2", "half"));}Output:
Skor tertinggi: 0Volume: 50Ok(150)Err(ParseIntError { kind: InvalidDigit })Kalau kamu yakin nilainya ada (misalnya angka yang baru saja kamu tulis sendiri di kode), .expect("alasan") juga boleh. Ia akan panic dengan pesanmu kalau ternyata kamu salah, jadi simpan untuk kasus yang memang tidak mungkin gagal. Memakai ? di main yang tidak mengembalikan apa-apa memberi E0277; lihat Operator ? dan Jalan pintas: unwrap_or.
Perbaikan 4: Pakai tipe yang polanya tidak bisa gagal
Section titled “Perbaikan 4: Pakai tipe yang polanya tidak bisa gagal”Pilih ini kalau datanya selalu punya bentuk yang sama, dan polanya gagal hanya karena tipe yang kamu pilih. Slice atau Vec bisa punya panjang berapa saja, tapi array seperti [u8; 3] selalu berisi tepat tiga item, jadi let [r, g, b] = color; selalu cocok. Tuple dan struct juga selalu cocok. Dan split_once memberimu kedua bagiannya sebagai satu tuple, jadi kamu cukup mengurus satu Option, bukan slice yang panjangnya tidak diketahui:
struct Point { x: i32, y: i32,}
fn main() { // array punya panjang tetap: pola ini selalu cocok let color: [u8; 3] = [255, 140, 0]; let [r, g, b] = color; println!("r={r} g={g} b={b}");
// tuple dan struct juga selalu cocok let (host, port) = ("localhost", 8080); let Point { x, y } = Point { x: 3, y: -1 }; println!("{host}:{port} di ({x}, {y})");
// split_once mengembalikan satu Option berisi kedua bagian let Some((key, value)) = "theme=dark".split_once('=') else { return; }; println!("{key} -> {value}");}Output:
r=255 g=140 b=0localhost:8080 di (3, -1)theme -> darkHal yang sama berlaku untuk enum buatanmu sendiri: let Shape::Circle(radius) = shape; gagal dengan pattern `Shape::Square(_)` not covered. Kalau nilainya memang hanya bisa berupa lingkaran, mungkin seharusnya itu struct Circle, bukan enum. Lihat Array dan Struct.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”Perulangan for atas Option. for Some(temp) in readings memberi “refutable pattern in for loop binding”. Perbaikannya: for temp in readings.iter().flatten() melewati semua None, atau lakukan perulangan atas setiap item dan pakai if let Some(t) = reading di dalamnya.
Parameter fungsi. fn greet(Some(name): Option<&str>) memberi “refutable pattern in function argument”. Perbaikannya: terima name: Option<&str> lalu match di dalam fungsi, atau terima name: &str dan biarkan pemanggil yang memutuskan apa yang dilakukan dengan None.
Mem-parse input. let Ok(n) = line.trim().parse::<i32>(); saat membaca input pengguna atau argumen command line. Perbaikannya: let Ok(n) = ... else { println!("Tolong ketik angka"); continue; }; di dalam loop input, atau match untuk mencetak error-nya.
Error terkait
Section titled “Error terkait”- E0004: non-exhaustive patterns: pemeriksaan “not covered” yang sama, untuk
match, bukanlet. - E0308: mismatched types: polanya sama sekali tidak cocok dengan tipenya, misalnya
let Some(x) = 5;. - E0277: trait bound not satisfied: yang kamu dapat saat memakai
?di fungsi yang tidak mengembalikanResultatauOption, sepertifn main()biasa. - E0282: type annotations needed: sering muncul di dekat
parse(), saat Rust tidak tahu tipe apa yang kamu mau. Tambahkan::<i32>seperti di contoh di atas. - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. let mana yang memberi E0005?
2. Apa yang harus dilakukan blok else pada let ... else?
3. Kamu butuh angka dari sebuah teks, dan 0 adalah default yang wajar kalau teksnya bukan angka. Apa perbaikan paling singkat?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0005 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini.
- Refutability — The Rust Book (bahasa Inggris): bab yang ditautkan rustc, tentang pola yang bisa gagal dan yang tidak.
- All the Places Patterns Can Be Used — The Rust Book (bahasa Inggris):
let,if let,while let, perulanganfor, dan parameter fungsi. - let-else — Rust by Example (bahasa Inggris): contoh singkat yang bisa dijalankan tentang
let ... else. - if let — Rust by Example (bahasa Inggris): kapan
if letlebih enak dibaca daripadamatchlengkap.