Di Chapter sebelumnya kita melihat bahwa fungsi dengan parameter String melahap kepemilikan nilainya, sehingga pemanggil harus menerima kembali nilainya lewat nilai kembalian jika masih ingin memakainya. Ritual ambil-dan-kembalikan seperti itu melelahkan ketika fungsi hanya perlu membaca data. Solusi Rust atas kelelahan ini adalah reference — penunjuk menuju nilai yang dimiliki orang lain — dan borrowing — tindakan meminjam nilai lewat reference tersebut. Kedua mekanisme ini membuat fungsi dapat memakai data tanpa mengambil alih kepemilikannya, dan sekaligus menjadi pintu masuk ke jaminan keamanan memori yang paling terkenal dari Rust.
Masalah yang Dipecahkan Referensi #
Kita mulai dari masalah nyatanya. Fungsi penghitung panjang teks secara harfiah hanya perlu membaca isi teks, tetapi dengan aturan ownership murni, fungsinya harus terlihat seperti ini: menerima String sebagai parameter, menghitung panjangnya, lalu mengembalikan String itu utuh agar pemanggil tidak kehilangan datanya. Nilai kembalian String pada fungsi penghitung panjang jelas tidak ada gunanya — angka panjangnya sudah diketahui, teksnya sendiri tidak diubah. Semakin banyak fungsi pembaca yang kita tulis, semakin banyak pula nilai yang hilir mudik tanpa makna antar fungsi.
Referensi memutus ritual tersebut. Alih-alih menyerahkan String utuh, pemanggil cukup menyerahkan alamat menuju nilai tersebut. Fungsi menerima tipe &String — yang dibaca “reference to String” — membaca isinya, lalu selesai. Kepemilikan tidak pernah berpindah, pemanggil tetap pemilik sah, dan tidak ada nilai kembalian ekstra.
Meminjam Nilai dengan Referensi #
Mari kita tulis ulang fungsi penghitung panjang dengan pola peminjaman:
fn text_length(text: &String) -> usize {
text.len()
}
fn main() {
let title = String::from("Memahami Ownership di Rust");
let length = text_length(&title);
println!("Judul '{}' punya {} karakter", title, length);
}Output yang Dihasilkan:
Judul 'Memahami Ownership di Rust' punya 26 karakterPerhatikan baris terakhir: title masih dicetak setelah dipakai oleh fungsi — inilah bukti bahwa kepemilikan tidak berpindah. Mari kita bedah kode di atas:
- Baris 1 (
fn text_length(text: &String) -> usize): tanda&sebelumStringmenyatakan bahwa parametertextadalah reference menuju sebuahString, bukanStringitu sendiri. Fungsi tidak menjadi pemilik; fungsi hanya peminjam. - Baris 2 (
text.len()): lewat reference,text.len()membaca panjang teks milik pemanggil. Operasi baca seperti ini sah untuk reference biasa. - Baris 6 (
let title = ...):titledimaintetap menjadi satu-satunya pemilik teks. - Baris 7 (
let length = text_length(&title);): ekspresi&titlemembuat reference menujutitle, lalu reference tersebut dikirim ke fungsi. Tindakan membuat dan mengirimkan reference ini disebut borrowing — kita meminjam nilai miliktitletanpa menyerahkan kepemilikannya. - Baris 9 (
println!("Judul '{}' ...", title, length);):titlemasih sah dipakai penuh karena tidak pernah kehilangan kepemilikan. Bandingkan dengan Chapter sebelumnya: pemanggilanprint_message(note)membuatnotemati, sedangkantext_length(&title)sama sekali tidak mengganggutitle.
Mekanisme Referensi di Balik Layar #
Secara mesin, reference adalah alamat memori berukuran tetap yang disimpan di stack — konsep yang sama dengan pointer di C, tetapi dengan jaminan yang tidak dimiliki pointer biasa. Saat text_length(&title) dipanggil, Rust menyalin alamat milik title ke slot parameter text. Karena alamat berukuran tetap, penyalinan ini semurah penyalinan i32 — jauh lebih murah daripada memindahkan atau meng-clone data heap.
Ketika fungsi berakhir, reference text hilang bersama parameternya, tetapi tidak ada proses drop atas data heap. Aturan ownership dari Chapter 2 menjelaskannya: yang di-drop adalah pemilik, dan text bukan pemilik — title-lah pemiliknya. Reference keluar scope dengan tenang tanpa memulangkan apa pun, seperti kunci ganda yang dikembalikan begitu saja karena bukan pemilik rumah.
Relasi antara pemilik dan peminjam dapat digambarkan sebagai berikut:
Diagram di atas menegaskan pembagian peran: panah penuh dari title menandakan kepemilikan sejati yang berujung pada tanggung jawab drop, sedangkan panah putus-putus dari reference text hanya menandakan akses baca sementara. Ketika fungsi usai, hanya panah putus-putus yang lenyap.
Kode yang Gagal: Lupa Tanda & #
Kesalahan paling awal yang dialami pemula adalah mencampuradukkan keduanya: fungsi meminta pinjaman, pemanggil mengirim kepemilikan. Kode berikut tidak dapat dikompilasi:
fn show_text(text: &String) {
println!("Teks: {}", text);
}
fn main() {
let message = String::from("Halo, Rust!");
show_text(message);
}Pesan error kompilator:
error[E0308]: mismatched types
--> main.rs:7:15
|
7 | show_text(message);
| --------- ^^^^^^^ expected `&String`, found `String`
| |
| arguments to this function are incorrect
|
note: function defined here
--> main.rs:1:4
|
1 | fn show_text(text: &String) {
| ^^^^^^^^^ -------------
help: consider borrowing here
|
7 | show_text(&message);
| +
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.Pesan E0308 menyatakan ketidakcocokan tipe secara gamblang: parameter menuntut &String, pemanggil mengirim String. Kompilator bahkan menuliskan perbaikannya — tambahkan tanda & sehingga pemanggilan menjadi show_text(&message);. Kebalikannya juga berlaku: fungsi bertipe parameter String yang dipanggil dengan &message akan menolak dengan pesan serupa. Tanda & bukan hiasan; tanda tersebut menentukan apakah kita mengirim kepemilikan atau sekadar pinjaman.
Kapan Meminjam, Kapan Memindahkan #
Dengan dua pola di tangan, kita dapat merumuskan pedoman memilih:
- Pinjam (
&) ketika fungsi hanya membaca atau menghitung sesuatu dari nilai — contohnya penghitung panjang, pencocok teks, atau validasi format. Pemanggil tidak kehilangan apa pun. - Pindahkan ketika fungsi memang memakan nilai untuk selamanya — misalnya fungsi yang menyimpan data ke koleksi internal atau membangun data baru dari data lama yang tidak dibutuhkan lagi.
- Clone hanya ketika dua salinan mandiri benar-benar diperlukan dan pinjaman tidak bisa dipakai.
Ringkasan #
Referensi memungkinkan fungsi memakai nilai tanpa mengambil kepemilikannya: pemanggil membuat &value, fungsi menerima &T, data heap tidak tersalin, dan pemanggil tetap bekerja dengan variabel aslinya setelah fungsi selesai. Kesalahan mencampur String dan &String menghasilkan E0308 lengkap dengan saran perbaikan satu karakter. Mekanisme pinjaman ini lahir dengan satu pertanyaan berikutnya: berapa banyak peminjam yang boleh beredar sekaligus, dan peminjam mana yang boleh mengubah data? Jawabannya adalah aturan borrowing pada Chapter selanjutnya.