Tuple mengemas beberapa nilai bertipe berbeda dalam satu kesatuan tanpa nama field, dan mengaksesnya lewat student.0 atau student.1 membuat makna kode bergantung pada hafalan posisi. Pola tuple memecahkan masalah ini dengan menguraikan setiap posisi langsung ke variabel bernama: satu baris pola, seluruh komponen terurai. Karena tuple adalah tipe komposit paling sederhana, polanya juga menjadi wadah latihan yang pas untuk kombinasi teknik sebelumnya — literal, binding, _, rentang, dan or-pattern semuanya dapat ditempatkan pada tiap posisi. Chapter ini membahas pola tuple pada let, match, perulangan for, hingga parameter fungsi, beserta galat ketika jumlah komponen pola tidak sesuai.

Memecah Tuple Menjadi Variabel #

Pola tuple menuliskan satu pola per posisi di dalam kurung, dan pencocokannya murni berdasarkan urutan: pola pertama untuk komponen pertama, pola kedua untuk komponen kedua, dan seterusnya. Bentuk paling umum adalah binding semua posisi:

rust
fn main() {
    let student = ("Budi", 87, 'A');

    let (name, score, grade) = student;

    println!("{} mendapat nilai {} (predikat {}).", name, score, grade);
}

Output yang Dihasilkan:

text
Budi mendapat nilai 87 (predikat A).

Mari kita bedah kode di atas:

  1. Baris 2 (let student = ("Budi", 87, 'A');): tuple tiga komponen bertipe (&str, i32, char); ketiga tipe tersebut Copy, sehingga tuple keseluruhannya juga Copy.
  2. Baris 4 (let (name, score, grade) = student;): pola menguraikan posisi demi posisi — "Budi" tersalin ke name, 87 ke score, dan 'A' ke grade; karena tuple bersifat Copy, variabel student tetap sah.
  3. Baris 6 (println!("{} mendapat nilai {} ...", ...);): ketiga variabel kini punya nama yang bermakna sehingga baris ini terbaca alami tanpa angka posisi.

Pola tuple juga menjadi idiom pertukaran nilai yang terkenal: let (b, a) = (a, b); menukar isi dua variabel dalam satu statement. Jumlah posisi pola wajib sama dengan jumlah komponen tuple — bagian akhir chapter ini memperlihatkan galat yang muncul apabila jumlahnya berbeda.

Menguji dan Mengabaikan Posisi Tertentu #

Setiap posisi pola tuple dapat diisi pola apa pun, bukan sekadar binding: literal menguji isi posisi, _ mengabaikannya, dan binding menampungnya. Dengan kombinasi ini, satu arm dapat menyaring kombinasi nilai yang rumit tanpa if tambahan di dalam body:

rust
fn main() {
    let request: (&str, u16, bool) = ("checkout", 502, true);

    match request {
        ("login", 200, _) => println!("Login berhasil."),
        ("login", code, _) => println!("Login gagal dengan kode {}.", code),
        (_, 200, _) => println!("Permintaan berhasil."),
        (action, code, retried) => {
            println!("{} gagal (kode {}), dicoba ulang: {}.", action, code, retried)
        }
    }
}

Output yang Dihasilkan:

text
checkout gagal (kode 502), dicoba ulang: true.

Mari kita telusuri pencocokan nilai ("checkout", 502, true):

  1. Baris 5 (("login", 200, _) => ...): dua posisi pertama diuji literal; "checkout" tidak sama dengan "login" sehingga arm ini dilewati bahkan sebelum posisi ketiga diperiksa.
  2. Baris 6 (("login", code, _) => ...): masih menuntut aksi "login" pada posisi pertama — nilai kita berbeda, arm dilewati lagi.
  3. Baris 7 ((_, 200, _) => ...): posisi pertama bebas nilai, tetapi posisi kedua menuntut 200; nilai 502 gagal pada uji ini.
  4. Baris 8–10 ((action, code, retried) => ...): arm penampung mengikat ketiga posisi — action menerima "checkout", code menerima 502, dan retried menerima true; body arm mencetak laporan lengkapnya.
  5. Baris 8 (arm terakhir): keberadaan arm murni binding membuat seluruh kombinasi nilai tertampung, sehingga match memenuhi exhaustiveness tanpa wildcard terpisah.

Pola Tuple Bersarang #

Tuple dapat berisi tuple lain, dan or-pattern dapat ditaruh per posisi. Kedua kemampuan ini memungkinkan pola yang menggali sekaligus menyaring kombinasi tanpa memperbanyak arm:

rust
fn main() {
    let board_cell = (2, 3);

    match board_cell {
        (0 | 2, 1 | 3) => println!("Sel gelap di pinggir papan."),
        (row, col) => println!("Sel biasa pada baris {}, kolom {}.", row, col),
    }

    let nested = ((1, 5), "detik");

    match nested {
        ((0, _), unit) => println!("Timer belum berjalan ({}).", unit),
        ((channel, duration), unit) => {
            println!("Kanal {} berjalan {} {}.", channel, duration, unit)
        }
    }
}

Output yang Dihasilkan:

text
Sel gelap di pinggir papan.
Kanal 1 berjalan 5 detik.

Mari kita bedah kedua pencocokan tersebut:

  1. Baris 5 ((0 | 2, 1 | 3) => ...): or-pattern bekerja per posisi — posisi pertama menerima 0 atau 2, posisi kedua menerima 1 atau 3; tanpa bentuk ini, kombinasi yang sama menuntut empat arm terpisah.
  2. Baris 6 ((row, col) => ...): arm penampung mengikat kedua posisi untuk sel yang tidak lolos saringan; nilai (2, 3) sendiri lolos di arm pertama karena 2 dan 3 memenuhi kedua or-pattern.
  3. Baris 9 (let nested = ((1, 5), "detik");): komponen pertama adalah tuple (1, 5), komponen kedua string satuan.
  4. Baris 12 (((0, _), unit) => ...): pola bersarang menguji komponen pertama dengan pola tuple (0, _) — kanal harus 0 dan durasi diabaikan — sementara komponen kedua diikat unit.
  5. Baris 13–14 (((channel, duration), unit) => ...): arm kedua menggali komponen pertama menjadi channel dan duration; nilai (1, 5) memenuhinya sehingga tercetak laporan kanal lengkap dengan satuan.

Pola Tuple di for dan Parameter Fungsi #

Penguraian tuple tidak eksklusif milik let dan match. Perulangan for menerima pola langsung pada nama perulangan, sehingga setiap elemen terurai begitu diambil. Parameter fungsi juga dapat memakai pola tuple, dan argumen terurai pada saat pemanggilan:

rust
fn main() {
    let students = [("Budi", 87), ("Siti", 92), ("Agus", 64)];

    for (name, score) in students {
        let status = match score {
            s if s >= 90 => "sangat baik",
            s if s >= 70 => "baik",
            _ => "perlu belajar lagi",
        };

        println!("{}: nilai {} ({}).", name, score, status);
    }
}

Output yang Dihasilkan:

text
Budi: nilai 87 (baik).
Siti: nilai 92 (sangat baik).
Agus: nilai 64 (perlu belajar lagi).

Mari kita bedah perannya:

  1. Baris 2 (let students = [...];): array berisi tiga tuple (&str, i32); karena setiap komponen Copy, elemen array juga Copy dan aman disalin per iterasi.
  2. Baris 4 (for (name, score) in students {): pola (name, score) ditempatkan pada posisi nama perulangan — setiap elemen terurai menjadi dua variabel sebelum badan perulangan dijalankan.
  3. Baris 5–9 (let status = match score { ... };): skor diolah dengan match berguard dari chapter match Lanjutan — bukti bahwa pola tuple dan guard dapat dipakai berdampingan dalam satu alur kerja.
  4. Baris 11 (println!("{}: nilai {} ({}).", name, score, status);): ketiga nilai — dua hasil uraian tuple dan satu hasil match — dicetak bersama untuk setiap siswa.

Bentuk pada parameter fungsi memakai ide yang sama:

rust
fn print_result((name, score): (&str, i32)) {
    println!("{} mendapat nilai {}.", name, score);
}

fn main() {
    print_result(("Siti", 92));
}

Output yang Dihasilkan:

text
Siti mendapat nilai 92.

Baris 1 (fn print_result((name, score): (&str, i32))) menuliskan pola tuple pada parameter: tipe (&str, i32) tetap dinyatakan penuh, sedangkan (name, score) menguraikan argumen saat fungsi dipanggil. Pola pada parameter harus selalu berhasil — argumen bertipe tuple dua komponen pasti cocok dengan pola dua posisi — dan banyak tim Rust justru lebih suka menguraikan di badan fungsi agar tanda tangan fungsi terbaca eksplisit; keduanya sah secara bahasa.

Kode yang Gagal: Jumlah Komponen Salah #

Pola tuple dengan jumlah posisi berbeda dari tuple aslinya ditolak sebelum program berjalan. Kompilator melaporkannya sebagai ketidakcocokan tipe karena pola dan nilai harus memiliki bentuk yang sama:

rust
fn main() {
    let measurement = (10, 20, 30);

    let (width, height) = measurement;

    println!("{} x {}", width, height);
}

Pesan error kompilator:

text
error[E0308]: mismatched types
 --> main.rs:4:9
  |
4 |     let (width, height) = measurement;
  |         ^^^^^^^^^^^^^^^   ----------- this expression has type `({integer}, {integer}, {integer})`
  |         |
  |         expected a tuple with 3 elements, found one with 2 elements
  |
  = note: expected tuple `({integer}, {integer}, {integer})`
             found tuple `(_, _)`

error: aborting due to 1 previous error

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

Pesan E0308 menjelaskan selisihnya dengan gamblang: nilai bertipe tuple tiga elemen, pola hanya menyediakan dua binding. Perbaikannya dapat memilih salah satu — mengikat komponen ketiga menjadi variabel, atau menuliskan _ pada posisi ketiga bila nilainya tidak diperlukan.

Ringkasan #

Pola tuple menguraikan komponen berdasarkan posisi: binding memberi nama pada tiap komponen, literal menguji isi posisi, _ mengabaikannya, dan or-pattern per posisi menyaring kombinasi tanpa memperbanyak arm. Pola bersarang menggali tuple di dalam tuple, for menerima pola langsung pada nama perulangan, dan parameter fungsi dapat menguraikan argumen bertipe tuple. Selisih jumlah komponen antara pola dan nilai memicu E0308 yang lengkap dengan perbandingan tipe. Setelah pola mampu menguraikan struct, enum, dan tuple, chapter berikutnya menyederhanakan satu kebutuhan yang sering muncul: menangani satu pola tertentu tanpa menulis match lengkap.