Pola rentang seperti 90..=100 menguji keanggotaan nilai, tetapi pola tersebut tidak menyimpan nilai yang lolos uji — arm harus membaca ulang variabel luar di badannya bila nilai itu ingin dipakai. Match guard dengan pengikatan (s if s >= 90 && s <= 100) menyelesaikan setengah masalah, namun menulis rentang sebagai dua perbandingan terasa dikerjakan dua kali. Operator @ menyatukan keduanya: pola s @ 90..=100 menguji rentang dan mengikat nilai yang cocok ke s dalam satu penulisan. Chapter ini membahas @ pada rentang, pada field varian enum, pada pengikatan nilai utuh bersama bagiannya, serta interaksinya dengan or-pattern yang menuntut tanda kurung.

Mengikat Nilai yang Lolos Uji Rentang #

Bentuk dasarnya adalah binding @ pola — nama variabel di kiri operator @, pola pengujian di kanannya. Kompilator mencocokkan nilai dengan pola di sisi kanan; apabila cocok, nilai yang sama diikat ke variabel di sisi kiri. Contoh berikut memetakan skor ujian ke predikat sambil tetap menyimpan skornya:

rust
fn main() {
    let score = 84;

    match score {
        s @ 90..=100 => println!("Skor {} berpredikat A.", s),
        s @ 80..=89 => println!("Skor {} berpredikat B.", s),
        s @ 70..=79 => println!("Skor {} berpredikat C.", s),
        s @ 0..=69 => println!("Skor {} mengulang materi.", s),
        _ => println!("Skor {} tidak sah.", score),
    }
}

Output yang Dihasilkan:

text
Skor 84 berpredikat B.

Mari kita bedah kode di atas:

  1. Baris 5 (s @ 90..=100 => ...): nilai dibandingkan dengan rentang 90..=100; apabila lolos, nilai tersebut diikat ke s — arm ini tidak cocok untuk 84, sehingga pencocokan turun.
  2. Baris 6 (s @ 80..=89 => ...): rentang 80..=89 menerima 84, dan s langsung berisi 84; badan arm mencetak skor tanpa membaca variabel score di luar match.
  3. Baris 7–8 (s @ 70..=79 dan s @ 0..=69): pola @ yang sama berulang untuk dua rentang berikutnya — setiap arm mandiri dengan bindingnya sendiri.
  4. Baris 9 (_ => println!("Skor {} tidak sah.", score);): wildcard tidak mengikat apa pun, sehingga nilai tidak sah dilaporkan lewat variabel luar score — perbedaan ini menegaskan bahwa @ adalah pengikatan, bukan sekadar pengujian.

Nilai i32 bersifat Copy, sehingga s menerima salinan skor dan variabel score tetap sah. Untuk data non-Copy, pengikatan @ mengikuti aturan kepemilikan yang berlaku pada pola biasa — pokok ini muncul kembali di bagian akhir chapter.

Mengikat Field Varian dengan @ #

Operator @ dapat ditempatkan pada posisi pola mana pun, termasuk field varian enum. Pola seat: s @ 1..=100 menguji field seat terhadap rentang sekaligus menyimpan nilainya ke s. Contoh berikut membagi area kursi berdasarkan nomornya:

rust
enum Ticket {
    Vip { seat: u16 },
    Regular { seat: u16 },
}

fn main() {
    let tickets = [Ticket::Vip { seat: 411 }, Ticket::Regular { seat: 85 }];

    for ticket in tickets {
        match ticket {
            Ticket::Vip { seat: s @ 1..=100 } => println!("VIP depan, kursi {}.", s),
            Ticket::Vip { seat: s @ 101..=500 } => println!("VIP tengah, kursi {}.", s),
            Ticket::Vip { seat } => println!("VIP belakang, kursi {}.", seat),
            Ticket::Regular { seat } => println!("Regular, kursi {}.", seat),
        }
    }
}

Output yang Dihasilkan:

text
VIP tengah, kursi 411.
Regular, kursi 85.

Mari kita uraikan polanya:

  1. Baris 11 (Ticket::Vip { seat: s @ 1..=100 } => ...): pola field memakai bentuk field: binding @ rentang — kursi VIP diuji terhadap 1..=100 dan hasilnya diikat ke s bila lolos.
  2. Baris 12 (Ticket::Vip { seat: s @ 101..=500 } => ...): rentang kedua menangkap nomor 411; binding s kembali memuat nilai yang cocok sehingga nomor kursi tercetak tanpa pengikatan terpisah.
  3. Baris 13 (Ticket::Vip { seat } => ...): arm penampung varian Vip tanpa rentang — kursi VIP di atas 500 diikat langsung oleh seat.
  4. Baris 14 (Ticket::Regular { seat } => ...): varian Regular memiliki pola field biasa; tiket kedua jatuh ke arm ini dan mencetak kursi 85.

Urutan arm kembali menentukan hasil: tiga arm Vip tersusun dari rentang paling awal hingga penampung terbuka, sehingga setiap nomor kursi menemukan arm yang tepat sebelum wildcard varian mana pun terlibat.

Mengikat Nilai Utuh dan Bagian Dalamnya #

Operator @ juga dapat mengikat seluruh nilai yang dicocokkan sementara pola di kanannya menguraikan bagian dalamnya. Pola s @ Some(qty) memeriksa varian Some, mengikat isi ke qty, dan mengikat nilai Option utuh ke s. Dikombinasikan dengan guard dari chapter match Lanjutan, satu arm dapat mengambil keputusan dari bagian dalam sekaligus melaporkan keadaan utuhnya:

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

    match stock {
        s @ Some(0) => println!("Nilai {:?} berarti stok habis.", s),
        s @ Some(qty) if qty <= 20 => println!("Nilai {:?} berarti stok tipis.", s),
        s @ Some(_) => println!("Nilai {:?} berarti stok aman.", s),
        s @ None => println!("Nilai {:?} berarti tidak dilacak.", s),
    }
}

Output yang Dihasilkan:

text
Nilai Some(12) berarti stok tipis.

Mari kita bedah keempat armnya:

  1. Baris 5 (s @ Some(0) => ...): s mengikat nilai Option utuh, sementara Some(0) menguji varian dan isinya — arm ini hanya cocok untuk Some(0) tepat; penanda {:?} mencetak bentuk Debug dari nilai utuh.
  2. Baris 6 (s @ Some(qty) if qty <= 20 => ...): tiga mekanisme bekerja sekaligus — s mengikat nilai utuh, qty mengikat isi Some, dan guard menguji isi terhadap 20; nilai Some(12) lolos ketiganya sehingga arm ini terpilih.
  3. Baris 7 (s @ Some(_) => ...): varian Some diuji tanpa mengikat isinya (_), nilai utuh tetap masuk ke s — arm ini menjadi penampung stok di atas 20.
  4. Baris 8 (s @ None => ...): varian None diikat utuh ke s, menggenapi cakupan seluruh kemungkinan Option<u32>.

Pada contoh di atas Option<u32> bersifat Copy sehingga pengikatan ganda tidak menimbulkan masalah kepemilikan. Untuk data non-Copy, mengikat nilai utuh dengan @ sekaligus mengikat bagian dalamnya by value akan ditolak kompilator dengan galat E0382 use of partially moved value — nilai tidak dapat dipindahkan dua kali ke dua binding berbeda. Solusinya adalah meminjam salah satunya dengan ref, teknik yang dibahas tuntas pada chapter berikutnya.

@ Bersama Or-Pattern #

Operator @ memiliki aturan penulisan khusus ketika pola di kanannya memuat or-pattern. Tanpa tanda kurung, r @ 1 | 2 terbaca sebagai (r @ 1) | 2 — pengikatan r hanya melekat pada alternatif pertama. Kompilator menolak ketidakseimbangan ini:

rust
fn main() {
    let rating = 4;

    match rating {
        r @ 1 | 2 => println!("Rating rendah: {}.", r),
        _ => println!("Rating lainnya."),
    }
}

Pesan error kompilator:

text
error[E0408]: variable `r` is not bound in all patterns
 --> main.rs:5:17
  |
5 |         r @ 1 | 2 => println!("Rating rendah: {}.", r),
  |         -       ^ pattern doesn't bind `r`
  |         |
  |         variable not in all patterns

error[E0381]: used binding `r` is possibly-uninitialized
 --> main.rs:5:53
  |
5 |         r @ 1 | 2 => println!("Rating rendah: {}.", r),
  |         -                                           ^ `r` used here but it is possibly-uninitialized
  |         |
  |         binding initialized here in some conditions
  |         binding declared here but left uninitialized

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0381, E0408.
For more information about an error, try `rustc --explain E0381`.

Dua galat ini saling melengkapi: E0408 menyatakan bahwa variabel r tidak terikat pada semua alternatif — alternatif 2 tidak mengikat apa pun — dan E0381 menyusul karena badan arm memakai r yang mungkin tidak pernah terisi saat alternatif kedua yang terpilih. Perbaikannya adalah membungkus or-pattern dengan tanda kurung sehingga @ mengikat hasil seluruh kelompok:

rust
fn main() {
    let rating = 2;

    match rating {
        r @ (1 | 2) => println!("Rating rendah: {}.", r),
        _ => println!("Rating lainnya."),
    }
}

Output yang Dihasilkan:

text
Rating rendah: 2.

Pada baris 5, pola r @ (1 | 2) kini utuh: nilai dibandingkan dengan or-pattern (1 | 2), dan nilai yang cocok — baik 1 maupun 2 — masuk ke r; nilai 2 pada program ini tercetak sebagai isi r.

Ringkasan #

Operator @ menjodohkan pengujian dan pengikatan: s @ 90..=100 menguji rentang dan menyimpan nilai yang cocok, seat: s @ 1..=100 menerapkannya pada field varian, dan s @ Some(qty) mengikat nilai utuh sekaligus bagiannya. Pengikatan @ mengikuti aturan kepemilikan — aman untuk tipe Copy, dan memerlukan ref ketika nilai utuh dan bagiannya sama-sama ingin dipegang pada data non-Copy. Bersama or-pattern, @ menuntut tanda kurung; tanpanya E0408 dan E0381 menandai pengikatan yang tidak merata. Chapter berikutnya membahas ref dan ref mut yang membuat pola mampu meminjam data tanpa mengambil kepemilikannya.