Semua penguraian yang kita lakukan sejauh ini mengikat data by value: field struct, isi varian Some, dan komponen tuple berpindah dari nilai asal ke variabel lokal, atau tersalin bila tipenya Copy. Perpindahan ini menjadi masalah ketika nilai asal masih ingin dipakai setelah pencocokan — data String di dalam Option lenyap bersama arm yang mengikatnya. Pola Rust punya dua kata kunci untuk keadaan ini: ref membuat binding berupa referensi biasa, dan ref mut membuat referensi mutable, keduanya dibangun langsung di dalam pola tanpa memindahkan data. Chapter ini membahas keduanya, lalu menutup dengan match ergonomics — perilaku modern yang membuat match pada referensi tidak lagi menuntut & di setiap pola — serta pembedaan arah kerja antara & dan ref di dalam pola.
match yang Memindahkan Data #
Mari kita lihat dulu masalahnya secara nyata. Program berikut mengikat isi Some dengan pola biasa, lalu mencoba memakai variabel nickname setelah match selesai:
fn main() {
let nickname: Option<String> = Some(String::from("Bud"));
match nickname {
Some(name) => println!("Nama panggilan: {}.", name),
None => println!("Tidak punya nama panggilan."),
}
println!("Variabel nickname: {:?}", nickname);
}Pesan error kompilator:
error[E0382]: borrow of partially moved value: `nickname`
--> main.rs:9:41
|
5 | Some(name) => println!("Nama panggilan: {}.", name),
| ---- value partially moved here
...
9 | println!("Variabel nickname: {:?}", nickname);
| ^^^^^^^^ value borrowed here after partial move
|
= note: partial move occurs because value has type `String`, which does not implement the `Copy` trait
help: borrow this binding in the pattern to avoid moving the value
|
5 | Some(ref name) => println!("Nama panggilan: {}.", name),
| +++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0382`.Pesan E0382 menjelaskan peristiwa partial move: pola Some(name) memindahkan isi String dari dalam nickname ke name, sehingga nickname tidak lagi utuh pada baris 9. Solusi yang ditawarkan kompilator pada blok help justru menjadi pokok bahasan kita: ganti pola menjadi Some(ref name).
ref: Meminjam di Dalam Pola #
Kata kunci ref diletakkan sebelum nama binding di dalam pola. Perannya membalik arah pengikatan: alih-alih memindahkan data ke binding, pola membuat binding bertipe referensi menuju data tersebut. Nilai asal tetap utuh dimiliki pemiliknya:
fn main() {
let nickname: Option<String> = Some(String::from("Bud"));
match nickname {
Some(ref name) => println!("Nama panggilan: {}.", name),
None => println!("Tidak punya nama panggilan."),
}
println!("Variabel nickname: {:?}", nickname);
}Output yang Dihasilkan:
Nama panggilan: Bud.
Variabel nickname: Some("Bud")Mari kita bedah kode di atas:
- Baris 5 (
Some(ref name) => ...): kata kuncirefmembuatnamebertipe&String— sebuah referensi menujuStringdi dalamnickname; data tidak berpindah,matchhanya meminjamnya selama arm berjalan. - Baris 6 (
None => ...): varian tanpa data tidak memerlukanref; arm ini tetap hadir demi exhaustiveness. - Baris 9 (
println!("Variabel nickname: {:?}", nickname);): baris ini kini sah —nicknametidak pernah kehilangan isinya, terbukti dari keluaranSome("Bud")yang tercetak utuh.
Mekanisme pinjamannya mengikuti aturan borrowing dari section Ownership: referensi yang dibuat pola bersifat immutable, hidup selama arm pemiliknya dievaluasi, dan tidak menghalangi pemanggilan pemilik data. Makro println! menerima &String tanpa kendala karena tipe tersebut mengimplementasikan Display melalui auto-deref.
ref mut: Mengubah Data lewat Pola #
Varian ref mut menghasilkan referensi mutable — pola tidak hanya membaca bagian yang diuraikan, tetapi juga dapat mengubahnya di tempat. Syaratnya mengikuti aturan biasa: variabel pemilik harus mut, dan hanya ada satu pinjaman mutable pada satu waktu:
fn main() {
let mut city: Option<String> = Some(String::from("Band"));
match city {
Some(ref mut name) => name.push_str("ung"),
None => println!("Kota belum diisi."),
}
println!("Variabel city: {:?}", city);
}Output yang Dihasilkan:
Variabel city: Some("Bandung")Mari kita uraikan baris-barisnya:
- Baris 2 (
let mut city: Option<String> = Some(String::from("Band"));): kata kuncimutpada variabel wajib hukumnya — data akan diubah melalui pinjaman yang dibuat pola. - Baris 5 (
Some(ref mut name) => name.push_str("ung");):namebertipe&mut String; methodpush_strmenambahkan"ung"pada data yang sama milikcity, tanpa kepemilikan berpindah ke arm. - Baris 9 (
println!("Variabel city: {:?}", city);): hasil perubahan terlihat dari luar —citykini memuatSome("Bandung")— bukti bahwa pola memang mengubah data di tempat, bukan salinannya.
Pinjaman &mut dari pola berlaku hanya selama arm tersebut dieksekusi; setelah match selesai, referensi itu hilang dan city dapat dipinjam pihak lain seperti biasa.
Match Ergonomics: match pada Referensi #
Sejak Rust 2018, cara yang lebih ringkas tersedia: cocokkan referensi dari sisi nilai dengan match &nilai, lalu tulis pola seolah-olah nilainya biasa. Kompilator menyesuaikan tipe binding secara otomatis — binding pada pola Some(name) menjadi &String tanpa perlu menulis ref:
fn main() {
let nickname: Option<String> = Some(String::from("Budi Santoso"));
match &nickname {
Some(name) => println!("Nama: {} ({} huruf).", name, name.len()),
None => println!("Tidak punya nama panggilan."),
}
println!("Variabel nickname masih sah: {:?}", nickname);
}Output yang Dihasilkan:
Nama: Budi Santoso (12 huruf).
Variabel nickname masih sah: Some("Budi Santoso")Mari kita bedah penyesuaiannya:
- Baris 4 (
match &nickname {): nilai yang dicocokkan adalah referensi&Option<String>— bukan nilai yang dimutahkan. - Baris 5 (
Some(name) => ...): pola ditulis seperti pencocokan biasa, tetapi match ergonomics membuatnamebertipe&Stringsecara otomatis;name.len()bekerja lewat auto-deref danprintln!mencetaknya langsung. - Baris 9 (
println!("Variabel nickname masih sah: {:?}", nickname);):nicknametetap sah karena hanya dipinjam — perilaku yang sama persis dengan versiref, ditulis lebih ringkas.
Perilaku penyesuaian otomatis ini disebut match ergonomics. Bagi sebagian besar kode modern, bentuk match &nilai inilah yang paling lazim; ref tetap bernilai ketika hanya sebagian arm yang ingin meminjam — arm lain tetap mengikat by value — atau ketika pola ditempatkan pada if let dan while let dengan scrutinee non-referensi.
& di Pola versus ref di Pola #
Dua tanda yang sering tertukar bekerja pada arah yang berlawanan. ref membuat referensi dari nilai biasa: scrutinee tetap dimiliki, binding menjadi &T. Sebaliknya, & yang ditulis di dalam pola menguraikan referensi yang sudah ada: scrutinee harus bertipe referensi, dan pola di dalamnya mencocokkan nilai yang ditunjuk. Program berikut memperlihatkan keduanya pada satu variabel:
fn main() {
let score = 78;
let score_ref = &score;
match score_ref {
n => println!("Tanpa tanda: binding bertipe referensi ({})", n),
}
match score_ref {
&n => println!("Dengan tanda &: referensi diuraikan ({})", n),
}
}Output yang Dihasilkan:
Tanpa tanda: binding bertipe referensi (78)
Dengan tanda &: referensi diuraikan (78)Mari kita bedah kedua bentuknya:
- Baris 3 (
let score_ref = &score;): variabelscore_refbertipe&i32— referensi menuju angka78. - Baris 5–6 (
match score_ref { n => ... }): pengikatan polos mengikuti tipe scrutinee, sehingganbertipe&i32; match ergonomics membuat bentuk ini sah tanpa tanda apa pun. - Baris 9–10 (
match score_ref { &n => ... }): tanda&pada pola membongkar referensi —nbertipei32dan berisi nilai aslinya; pencocokan ini sah karenai32bersifatCopy, sedangkan pada tipe non-Copypembongkaran&akan mencoba memindahkan data keluar dari referensi dan ditolak oleh kompilator.
Ringkasan #
Pengikatan pola by value memindahkan data non-Copy dan menghanguskan nilai asalnya — galat E0382 menandainya lengkap dengan saran ref. Kata kunci ref mengubah binding menjadi pinjaman &T, ref mut mengubahnya menjadi &mut T yang dapat mengubah data di tempat, dan match &nilai mencapai efek ref secara lebih ringkas lewat match ergonomics. Tanda & di dalam pola bekerja pada arah berlawanan — membongkar referensi yang sudah ada. Dengan semua perkakas pola kini lengkap, chapter terakhir section ini membahas jaminan yang menjaganya: exhaustiveness checking yang memastikan tidak ada kemungkinan nilai yang lolos tanpa tertangani.