Lewati ke konten

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.

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 covered

Ada dua kata kunci di sini:

  • Pola refutable (bisa gagal) adalah pola yang mungkin tidak cocok. Some(top) gagal kalau nilainya None. Ok(age) gagal pada Err. [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 seperti let (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.

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.

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: 95
Port: 3000
'abc' bukan port yang valid, memakai 8080
Port: 8080
▶ Coba sendiri

Jangan 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: 42
Tidak bisa membaca 'forty-two': invalid digit found in string
host = localhost, port = 8080
'localhost' seharusnya berbentuk host:port
Hai, orang asing!
▶ Coba sendiri

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 sini
fn 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: 0
Volume: 50
Ok(150)
Err(ParseIntError { kind: InvalidDigit })
▶ Coba sendiri

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=0
localhost:8080 di (3, -1)
theme -> dark
▶ Coba sendiri

Hal 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.

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.

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