Error Rust E0004: non-exhaustive patterns: not covered (match belum lengkap)
E0004 artinya match-mu melupakan setidaknya satu kemungkinan nilai, dan Rust menolak menebak apa yang harus terjadi dalam kasus itu. Ini salah satu error paling ramah di Rust: compiler memberi tahu persis kasus mana yang hilang, bahkan menuliskan barisnya untukmu. Halaman ini menunjukkan perbaikannya dulu, baru kemudian menjelaskan kenapa pemeriksaan ini termasuk fitur terbaik Rust. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.
Pesan error-nya
Section titled “Pesan error-nya”error[E0004]: non-exhaustive patterns: `OrderStatus::Delivered` not covered --> src/main.rs:8:11 | 8 | match status { | ^^^^^^ pattern `OrderStatus::Delivered` not covered |note: `OrderStatus` defined here --> src/main.rs:1:6 | 1 | enum OrderStatus { | ^^^^^^^^^^^... 4 | Delivered, // added later | --------- not covered = note: the matched value is of type `OrderStatus`help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown |10 ~ OrderStatus::Shipped => "on its way",11 ~ OrderStatus::Delivered => todo!(), |Compiler bilang pola OrderStatus::Delivered belum ditangani, menunjukkan di mana varian itu didefinisikan, dan menyarankan arm yang perlu ditambahkan.
Ini program yang menghasilkan error tersebut:
enum OrderStatus { Pending, Shipped, Delivered, // added later}
fn describe(status: OrderStatus) -> &'static str { match status { OrderStatus::Pending => "waiting to be packed", OrderStatus::Shipped => "on its way", // ❌ nothing for Delivered }}
fn main() { println!("{}", describe(OrderStatus::Delivered));}(Komentarnya berarti “ditambahkan belakangan” dan “tidak ada apa-apa untuk Delivered”.) Teks di antara backtick berubah sesuai kodemu. Kamu mungkin melihat `None` not covered, `&_` not covered, `0_u8` and `4_u8..=u8::MAX` not covered atau `(true, false)` and `(false, true)` not covered. Error-nya tetap sama: Rust sedang mendaftar nilai-nilai yang belum punya arm di match-mu.
Apa artinya
Section titled “Apa artinya”Baca pesannya dari atas ke bawah:
- Baris 8: tanda
^^^^^^menunjuk nilai yang sedang kamu cocokkan, yaitustatus. - “pattern
OrderStatus::Deliverednot covered” (pola ini belum ditangani): inilah kasus yang kamu lupakan. - Bagian note melompat ke definisi enum dan menandai varian yang terlupa di baris 4.
- Bagian help menunjukkan arm persis yang perlu ditambahkan.
todo!()hanya penanda sementara; ganti dengan kode sungguhan.
Bayangkan mesin penjual minuman dengan tiga tombol, tapi hanya dua yang tersambung kabelnya. Kalau tombol ketiga ditekan, lalu… apa? Rust tidak mengizinkanmu mengirim mesin dengan tombol mati. Setiap tombol butuh jawaban, meskipun jawabannya “sedang rusak”.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”match di Rust harus exhaustive (lengkap): untuk setiap nilai yang bisa dimiliki tipenya, tepat satu arm harus berlaku. Kalau ada arm yang hilang, program akan sampai di titik yang tidak punya kode untuk dijalankan. Bahasa lain diam-diam melewatinya, mengembalikan nilai default, atau crash saat runtime. Rust menangkapnya sebelum program sempat berjalan. Dasarnya ada di pelajaran match.
Manfaat sebenarnya baru terasa nanti, saat kodemu bertambah besar. Misalnya kamu menambahkan varian baru ke sebuah enum yang di-match di sepuluh tempat di proyekmu:
enum OrderStatus { Pending, Shipped, Delivered, Returned, // baru!}Sekarang compiler memberimu satu E0004 untuk setiap match yang perlu diperbarui:
error[E0004]: non-exhaustive patterns: `&OrderStatus::Returned` not covered --> src/main.rs:9:11error[E0004]: non-exhaustive patterns: `&OrderStatus::Returned` not covered --> src/main.rs:17:11Itu daftar tugas yang sudah ditulis untukmu. Kamu tidak perlu menyisir seluruh kode sambil berharap semua tempat sudah ketemu. Inilah kenapa developer Rust yang berpengalaman sangat suka enum: kamu bisa mengubahnya tanpa rasa takut.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Tambahkan arm yang hilang
Section titled “Perbaikan 1: Tambahkan arm yang hilang”Pilih ini untuk enum buatanmu sendiri. Hampir selalu inilah perbaikan yang tepat, dan memang persis itu yang disarankan help: dari rustc. Tambahkan arm-nya, lalu ganti todo!() dengan apa yang seharusnya terjadi:
enum OrderStatus { Pending, Shipped, Delivered,}
fn describe(status: OrderStatus) -> &'static str { match status { OrderStatus::Pending => "menunggu dikemas", OrderStatus::Shipped => "sedang dikirim", OrderStatus::Delivered => "sudah sampai di depan pintu", // ✅ semua kasus ditangani }}
fn main() { println!("{}", describe(OrderStatus::Pending)); println!("{}", describe(OrderStatus::Delivered));}Output:
menunggu dikemassudah sampai di depan pintuKalau beberapa varian harus melakukan hal yang sama, gabungkan dengan | dalam satu arm: OrderStatus::Shipped | OrderStatus::Delivered => false. Kamu tetap menyebut setiap varian, jadi compiler tetap bisa mengingatkanmu soal varian baru. Hal yang sama berlaku untuk Option: kalau error-nya bilang `None` not covered, tambahkan arm None => .... Lihat Option.
Perbaikan 2: Pakai rentang dan _ untuk angka dan teks
Section titled “Perbaikan 2: Pakai rentang dan _ untuk angka dan teks”Pilih ini kalau kamu mencocokkan integer, char, atau string. u8 punya 256 kemungkinan nilai dan &str punya tak terhingga, jadi kamu tidak mungkin mendaftar semuanya. Pakai rentang seperti 1..=9 untuk sekelompok angka, dan akhiri dengan _ (atau nama seperti other) untuk “sisanya”:
fn grade(score: u8) -> char { match score { 90..=100 => 'A', 75..=89 => 'B', 50..=74 => 'C', 0..=49 => 'F', _ => '?', // 101 sampai 255: bukan nilai yang valid }}
fn run(command: &str) { match command { "start" => println!("Memulai timer"), "stop" => println!("Menghentikan timer"), other => println!("Perintah tidak dikenal: {other}"), }}
fn main() { println!("{} {} {}", grade(95), grade(62), grade(150)); run("start"); run("pause");}Output:
A C ?Memulai timerPerintah tidak dikenal: pauseUntuk string, rustc bahkan mengatakannya langsung: `&str` cannot be matched exhaustively, so a wildcard `_` is necessary (&str tidak bisa dicocokkan secara lengkap, jadi wildcard _ wajib ada). Untuk angka, Rust memeriksa rentangmu dengan teliti. Kalau rentangnya benar-benar mencakup setiap nilai (misalnya dari 0..=49 sampai 90..=u8::MAX), kamu sama sekali tidak butuh _. Selengkapnya di Mencocokkan sebuah rentang.
Perbaikan 3: Pakai if let atau let else kalau hanya satu kasus yang penting
Section titled “Perbaikan 3: Pakai if let atau let else kalau hanya satu kasus yang penting”Pilih ini kalau kamu hanya ingin bertindak untuk satu pola dan mengabaikan sisanya. match lengkap dengan arm kosong “tidak melakukan apa-apa” memang bisa, tapi if let menyampaikannya dengan lebih jelas. let else cocok untuk “ambil nilainya, atau keluar dari fungsi lebih awal”:
fn greet(nickname: Option<&str>) { if let Some(name) = nickname { println!("Hai, {name}!"); } // tidak ada nama panggilan: tidak melakukan apa-apa}
fn first_word_len(text: &str) -> usize { let Some(word) = text.split_whitespace().next() else { return 0; // teks kosong: keluar lebih awal }; word.len()}
fn main() { greet(Some("Ferris")); greet(None); println!("{}", first_word_len("hello world")); println!("{}", first_word_len(" "));}Output:
Hai, Ferris!50("hello world" sengaja dibiarkan dalam bahasa Inggris karena output-nya adalah panjang kata pertamanya.) Blok else pada let else harus keluar dari blok saat ini (return, break, continue, atau panic). Lihat if let dan let-else.
Perbaikan 4: Penampung _ => untuk sisanya (dengan konsekuensi)
Section titled “Perbaikan 4: Penampung _ => untuk sisanya (dengan konsekuensi)”Pilih ini kalau sebagian besar varian sebuah enum memang melakukan hal yang sama. _ cocok dengan apa pun yang tidak ditangkap oleh arm-arm di atasnya:
enum Key { Up, Down, Left, Right, Enter, Escape,}
fn handle(key: Key) { match key { Key::Enter => println!("Konfirmasi"), Key::Escape => println!("Batal"), _ => println!("Gerakkan kursor"), }}
fn main() { handle(Key::Enter); handle(Key::Left); handle(Key::Escape);}Output:
KonfirmasiGerakkan kursorBatalJujur saja soal harganya: _ mematikan pemeriksaan yang dibahas di Kenapa Rust melakukan ini. Kalau nanti kamu menambahkan Key::Delete, compiler diam saja, dan menekan Delete malah menggerakkan kursor. Itu bug yang sebenarnya bisa ditangkap Rust untukmu. Untuk enum buatanmu sendiri, lebih baik daftar variannya dengan | (Key::Up | Key::Down | Key::Left | Key::Right => ...). Simpan _ untuk angka, string, dan enum dari crate lain.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”Option tanpa None. match nickname { Some(name) => ... } menghasilkan `None` not covered. Perbaikannya: tambahkan arm None =>, atau pakai if let Some(name) = nickname kalau tidak ada yang perlu dilakukan untuk None.
Tuple dan boolean. Mencocokkan dua bool sekaligus punya empat kombinasi, dan gampang sekali melupakan sebagian:
match (logged_in, is_admin) { (true, true) => println!("Selamat datang, admin"), (false, false) => println!("Silakan login dulu"),} // ❌ patterns `(true, false)` and `(false, true)` not coveredPerbaikannya: tambahkan arm yang hilang, atau pakai _ di dalam tuple, seperti (false, _) => println!("Silakan login dulu"), yang mencakup kedua kasus “belum login”. Lihat Mencocokkan beberapa nilai sekaligus.
Pola di dalam let. Menulis let Some(score) = saved; adalah let yang mungkin tidak cocok. Ini error “saudaranya”, E0005, dengan ide yang sama:
error[E0005]: refutable pattern in local binding --> src/main.rs:3:9 |3 | let Some(score) = saved; | ^^^^^^^^^^^ pattern `None` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one varianthelp: you might want to use `let...else` to handle the variant that isn't matched |3 | let Some(score) = saved else { todo!() }; | ++++++++++++++++Compiler bilang let butuh pola yang pasti cocok (“irrefutable pattern”), dan menyarankan let...else untuk menangani varian yang tidak cocok. Perbaikannya: let Some(score) = saved else { return; };, atau if let, seperti di Perbaikan 3. Lihat Pola di let.
Enum dari crate lain. Beberapa enum, seperti std::io::ErrorKind, ditandai #[non_exhaustive]. Pembuatnya mungkin menambahkan varian nanti, jadi meskipun kamu sudah mendaftar setiap varian yang ada hari ini, rustc tetap bilang `_` not covered. Perbaikannya: tambahkan arm _ =>. Di sini itu wajib, bukan jalan pintas.
Error terkait
Section titled “Error terkait”- E0005 (refutable pattern in local binding, pola yang bisa gagal di
let): pemeriksaan “not covered” yang sama, untuklet, bukanmatch. Contohnya ada di atas. - E0308: tipe tidak cocok, misalnya kalau arm-arm
matchmengembalikan tipe yang berbeda ("text"di satu arm, angka di arm lain). - E0382: borrow of partially moved value, kalau arm seperti
Some(name)memindahkanStringkeluar dan kamu memakai nilai aslinya sesudahnya. Cocokkan&nicknamesaja. - E0507: cannot move out of a borrowed value, kalau arm
matchmencoba mengambilStringdari sesuatu yang hanya kamu pinjam. - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Kamu menambahkan varian Refunded ke sebuah enum. Apa yang dilakukan compiler terhadap ekspresi match yang sudah ada dan menyebut setiap varian dengan namanya?
2. Kamu mencocokkan &str dengan "yes" dan "no". Bagaimana memperbaiki E0004?
3. Apa kekurangan _ => di match atas enum buatanmu sendiri?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0004 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini.
- E0005 — Rust error codes index (bahasa Inggris): error “saudaranya” untuk pola di
let. - The match Control Flow Construct — The Rust Book (bahasa Inggris): bab yang menjelaskan kenapa
matchharus lengkap, dan placeholder_. - Refutability — The Rust Book (bahasa Inggris): kapan sebuah pola bisa gagal cocok, dan di mana hal itu diizinkan.
- match — Rust by Example (bahasa Inggris): contoh singkat yang bisa dijalankan tentang rentang,
|, dan_di armmatch.