Enum berbeda dari struct: satu enum dapat memuat beberapa varian dengan bentuk data yang berbeda-beda — ada varian tanpa data, varian dengan field bernama, dan varian dengan data posisi. Kekuatan match yang sesungguhnya baru terasa ketika pola tidak hanya memilih varian, tetapi juga menguraikan data di dalamnya pada arm yang sama. Chapter ini membahas penguraian ketiga bentuk varian tersebut, penggunaan pada Option, pola enum bersarang, cara mengabaikan sebagian isi varian, serta galat yang muncul ketika jumlah data pada pola tidak sesuai definisi varian.

Satu Arm untuk Setiap Bentuk Varian #

Setiap bentuk varian menuntut bentuk pola yang mengikutinya. Varian tanpa data cukup dituliskan path-nya saja, varian seperti struct memakai kurung kurawal berisi field, dan varian seperti tuple memakai kurung biasa berisi pola per posisi. Satu match dapat menampung ketiganya sekaligus, dan setiap data yang diuraikan langsung menjadi variabel lokal di dalam arm pemiliknya.

rust
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

fn handle(message: Message) {
    match message {
        Message::Quit => println!("Sesi diakhiri."),
        Message::Move { x, y } => println!("Pindah ke koordinat ({}, {}).", x, y),
        Message::Write(text) => println!("Pesan terkirim: {}", text),
        Message::ChangeColor(r, g, b) => println!("Warna diubah ke rgb({}, {}, {}).", r, g, b),
    }
}

fn main() {
    handle(Message::Quit);
    handle(Message::Move { x: 10, y: -4 });
    handle(Message::Write(String::from("Halo, Budi!")));
    handle(Message::ChangeColor(10, 200, 60));
}

Output yang Dihasilkan:

text
Sesi diakhiri.
Pindah ke koordinat (10, -4).
Pesan terkirim: Halo, Budi!
Warna diubah ke rgb(10, 200, 60).

Mari kita bedah pola pada setiap arm:

  1. Baris 1–6 (enum Message { ... }): enum mendemonstrasikan tiga bentuk varian — Quit tanpa data, Move dengan field bernama, Write dan ChangeColor dengan data posisi.
  2. Baris 9 (match message {): parameter message menjadi nilai yang dicocokkan; kepemilikan nilainya berada di tangan fungsi handle.
  3. Baris 11 (Message::Quit => ...): varian tanpa data tidak butuh kurung — path Message::Quit sendiri adalah polanya.
  4. Baris 12 (Message::Move { x, y } => ...): varian bentuk struct diuraikan seperti pola struct; field x dan y menjadi variabel lokal di dalam arm ini.
  5. Baris 13 (Message::Write(text) => ...): varian tuple satu posisi diuraikan ke binding text bertipe String; karena String tidak Copy, isi teks berpindah dari message ke text.
  6. Baris 14 (Message::ChangeColor(r, g, b) => ...): tiga posisi data diikat menjadi r, g, dan b — urutan pola mengikuti urutan data pada varian.
  7. Baris 18–21 (handle(...)): empat pemanggilan menguji keempat arm; masing-masing menghasilkan satu baris keluaran sesuai varian yang dikirim.

Pengikatan data varian bersifat by value: pada arm Write, teks berpindah ke text dan message habis terpakai. Ketika data perlu dipakai ulang tanpa berpindah, pola dapat meminjamkannya lewat ref yang akan kita bahas pada chapter ref dan ref mut di Pola.

Menguraikan Option #

Option<T> adalah enum dua varian — Some(T) membawa satu data dan None tanpa data — sehingga seluruh teknik penguraian varian berlaku langsung. Posisi data di dalam Some dapat diisi pola apa pun, termasuk literal yang menguji isi tanpa mengikatnya:

rust
fn main() {
    let stock: Option<u32> = Some(12);

    match stock {
        Some(0) => println!("Stok kosong, segera isi ulang."),
        Some(qty) => println!("Stok tersedia: {} unit.", qty),
        None => println!("Produk tidak dilacak."),
    }
}

Output yang Dihasilkan:

text
Stok tersedia: 12 unit.

Mari kita telaah pencocokannya:

  1. Baris 2 (let stock: Option<u32> = Some(12);): variabel stock memegang varian Some berisi angka 12; anotasi tipe u32 menentukan tipe data di dalam varian.
  2. Baris 5 (Some(0) => ...): pola menguji varian Some sekaligus literal 0 di dalamnya — hanya cocok bila isinya tepat nol; nilai 12 gagal pada uji ini.
  3. Baris 6 (Some(qty) => ...): pola mengikat isi Some ke variabel qty; nilai 12 berpindah ke qty sehingga arm ini terpilih.
  4. Baris 7 (None => ...): varian tanpa data menutup kemungkinan terakhir; ketiganya wajib ada agar match memenuhi exhaustiveness.

Pola Enum Bersarang #

Data di dalam varian dapat berupa enum lain. Pola bersarang menggali lapisan demi lapisan dalam satu penulisan: Some(Some(value)) menguji varian Some pada lapisan luar sekaligus varian Some pada lapisan dalam, baru mengikat value pada intinya.

rust
fn main() {
    let backup: Option<Option<u32>> = Some(None);

    match backup {
        Some(Some(value)) => println!("Cadangan berisi {}.", value),
        Some(None) => println!("Slot cadangan ada, tetapi kosong."),
        None => println!("Tidak ada slot cadangan."),
    }
}

Output yang Dihasilkan:

text
Slot cadangan ada, tetapi kosong.

Mari kita bedah ketiga armnya:

  1. Baris 2 (let backup: Option<Option<u32>> = Some(None);): lapisan luar Some membungkus None — slot cadangan tersedia tetapi kosong.
  2. Baris 5 (Some(Some(value)) => ...): pola paling dalam menguji Some dua kali; arm ini tercapai bila cadangan benar-benar berisi angka, lalu value mengikat angka tersebut.
  3. Baris 6 (Some(None) => ...): lapisan luar Some, lapisan dalam None — persis kondisi data kita, sehingga baris inilah yang tercetak.
  4. Baris 7 (None => ...): pola untuk slot yang sama sekali tidak ada; tiga arm ini membagi seluruh ruang nilai Option<Option<u32>> tanpa sisa.

Mengabaikan Sebagian Isi Varian #

Ketika hanya sebagian data varian yang dibutuhkan, dua alat bantu siap dipakai: .. mengabaikan seluruh field varian bentuk struct, dan _ mengabaikan posisi tertentu pada varian bentuk tuple. Or-pattern | dapat menyatukan varian-varian yang perlakuannya sama, sepanjang setiap alternatif mengikat nama yang konsisten — varian tanpa binding seperti Message::Quit dan Message::Move { .. } aman digabung:

rust
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

fn summarize(message: Message) {
    match message {
        Message::Quit | Message::Move { .. } => println!("Pesan tanpa data yang perlu dicetak."),
        Message::Write(text) => println!("Pesan: {}", text),
        Message::ChangeColor(red, _, _) => println!("Komponen merah: {}", red),
    }
}

fn main() {
    summarize(Message::Quit);
    summarize(Message::Move { x: 10, y: -4 });
    summarize(Message::Write(String::from("Halo, Budi!")));
    summarize(Message::ChangeColor(10, 200, 60));
}

Output yang Dihasilkan:

text
Pesan tanpa data yang perlu dicetak.
Pesan tanpa data yang perlu dicetak.
Pesan: Halo, Budi!
Komponen merah: 10

Mari kita uraikan pola-polanya:

  1. Baris 10 (Message::Quit | Message::Move { .. } => ...): or-pattern menyatukan dua varian; pola Move { .. } memilih varian Move tanpa mengikat x dan y, sehingga kedua varian dapat berbagi satu arm.
  2. Baris 11 (Message::Write(text) => ...): varian ini tetap mengikat isi teksnya karena isinya memang dibutuhkan — pengabaian dan pengikatan dapat berdampingan dalam satu match.
  3. Baris 12 (Message::ChangeColor(red, _, _) => ...): hanya posisi pertama yang diikat menjadi red; dua _ mengabaikan komponen hijau dan biru.
  4. Baris 16–17 (summarize(Message::Quit); summarize(...)): dua varian berbeda — Quit dan Move — jatuh ke arm yang sama, terlihat dari dua baris keluaran pertama yang identik.

Kode yang Gagal: Jumlah Data Varian Salah #

Varian bentuk tuple menuntut pola dengan jumlah posisi yang persis sama dengan definisinya. Satu posisi kurang atau lebih langsung ditolak kompilator dengan galat E0023:

rust
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

fn main() {
    let message = Message::ChangeColor(10, 200, 60);

    match message {
        Message::Quit => println!("Sesi diakhiri."),
        Message::Move { x, y } => println!("Pindah ke ({}, {}).", x, y),
        Message::Write(text) => println!("Pesan: {}", text),
        Message::ChangeColor(r, g) => println!("Warna: rgb({}, {}).", r, g),
    }
}

Pesan error kompilator:

text
error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 3 fields
  --> main.rs:15:30
   |
 5 |     ChangeColor(u8, u8, u8),
   |                 --  --  -- tuple variant has 3 fields
...
15 |         Message::ChangeColor(r, g) => println!("Warna: rgb({}, {}).", r, g),
   |                              ^  ^ expected 3 fields, found 2
   |
help: use `_` to explicitly ignore each field
   |
15 |         Message::ChangeColor(r, g, _) => println!("Warna: rgb({}, {}).", r, g),
   |                                  +++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0023`.

Pesan error mengutip dua lokasi sekaligus: baris 5 tempat varian didefinisikan dengan tiga field, dan baris 15 tempat pola hanya memuat dua binding. Saran perbaikannya lugas — tambahkan _ untuk posisi yang tidak diperlukan. Aturan serupa berlaku pada varian bentuk struct: melupakan satu field pada pola Message::Move { x } memicu E0027 pattern does not mention field y, persis seperti pola struct biasa pada chapter sebelumnya.

Ringkasan #

Pola enum mengikuti bentuk varianya: varian tanpa data cukup path, varian struct diuraikan dengan field bernama, varian tuple diikat per posisi, dan lapisan enum di dalam enum diuraikan dengan pola bersarang. Posisi data dapat diisi literal untuk menguji isi, _ untuk mengabaikan, atau binding untuk memindahkan data ke arm. Or-pattern menyatukan varian-varian berperlakuan sama, dan jumlah data pola yang tidak sesuai definisi memicu E0023 lengkap dengan saran perbaikan. Chapter berikutnya menguraikan tipe komposit yang paling sederhana: tuple beserta pola posisinya.