Kita kini memegang dua cara menyerahkan data ke fungsi: meminjam potongannya lewat slice, atau memindahkan kepemilikannya secara penuh. Keduanya sah, keduanya aman, dan kompilator tidak akan menyalahkan pilihan mana pun selama aturannya dihormati — tetapi pilihan yang tepat membuat program hemat, fleksibel, dan mudah dikembangkan, sementara pilihan yang salah menambah alokasi tidak perlu atau memaksa pemanggil membuat salinan. Chapter penutup section ini tidak memperkenalkan mekanisme baru; kita akan merangkum seluruh bekal menjadi pedoman keputusan, menguji dua fungsi dengan dua strategi berbeda, membaca E0505 untuk jebakan memindahkan pemilik saat dipinjam, lalu menutup section dengan rangkuman besar.
Dua Pertanyaan Kunci #
Setiap keputusan pinjam-versus-miliki sebenarnya bermuara pada dua pertanyaan sederhana. Pertama: apakah penerima data hanya perlu membaca — atau menghitung, mencari, memformat — tanpa menyimpan datanya untuk kemudian hari? Jika ya, slice adalah jawaban default-nya: &str untuk teks, &[T] untuk data berurutan lain. Kedua: apakah penerima perlu memiliki data lebih lama dari masa pemanggilan fungsi — misalnya menyimpannya ke koleksi internal, atau mengembalikan data baru yang berdiri sendiri? Jika ya, kepemilikan harus berpindah, dan parameter sebaiknya menerima String, Vec, atau nilai utuh lainnya.
Pertanyaan pertama menempatkan slice pada posisi terhormat di Rust. Fungsi penghitung, pencari, pembanding, dan pembaca umumnya hanya butuh membaca; dengan parameter slice, fungsi-fungsi itu sekalian memperoleh keluwesan menerima banyak jenis wadah — seperti yang kita buktikan di Chapter Slice sebagai Parameter. Pertanyaan kedua mengingatkan bahwa pinjaman selalu berumur pendek: data yang harus hidup setelah pemanggilnya selesai tidak boleh tetap berupa slice yang menumpang.
Dua Fungsi, Dua Strategi #
Kita letakkan kedua strategi berdampingan pada satu program. Fungsi pertama membangun teks baru dan mengembalikan kepemilikannya; fungsi kedua hanya meminjamkan potongan dari teks pemanggil:
fn owned_greeting(name: &str) -> String {
let greeting = format!("Halo, {}! Selamat belajar.", name);
greeting
}
fn borrowed_initial(name: &str) -> &str {
&name[..1]
}
fn main() {
let name = String::from("Budi");
let greeting = owned_greeting(&name);
let initial = borrowed_initial(&name);
println!("{}", greeting);
println!("Inisial: {}", initial);
}Output yang Dihasilkan:
Halo, Budi! Selamat belajar.
Inisial: BMari kita bedah kedua strategi tersebut:
- Baris 1 (
fn owned_greeting(name: &str) -> String): strategi pertama — pinjam bahan bacanya, hasilnya dimiliki baru. Parameter tetap&stragar fungsi menerima teks dari sumber mana pun, tetapi nilai kembalianStringmenyatakan hasil keluaran berdiri sendiri. - Baris 2 (
let greeting = format!("Halo, {}! Selamat belajar.", name);):format!— macro yang sudah kita kenal dari section Ownership — mengalokasikanStringbaru di heap. Teks salam itu bukan potongan dariname; teks itu milik fungsi, lalu kepemilikannya berpindah ke pemanggil. - Baris 6 (
fn borrowed_initial(name: &str) -> &str): strategi kedua — hasil keluaran adalah potongan pinjaman dari parameter. Tidak ada alokasi baru sama sekali; fungsi hanya mengembalikan jendela berukuran satu byte. - Baris 7 (
&name[..1]): potongan[..1]meminjam byte pertama teks — hurufB. Selamainitialdipakai, teks miliknameterjaga tidak boleh diubah, karena itulah syarat pinjaman. - Baris 11–14 (
let name ...dst.):namedipinjam oleh kedua fungsi tanpa kehilangan kepemilikan sedikit pun.greetingmenjadi pemilik salam baru;initialtetap hanya peminjam potongan dariname.
Kapan memilih yang mana? owned_greeting tepat ketika hasilnya harus hidup mandiri — disimpan di struct, dikirim ke fungsi lain yang meminta String, atau bertahan setelah teks sumbernya tidak relevan lagi. borrowed_initial tepat ketika hasilnya hanya dipakai sementara di scope yang sama dengan pemilik datanya — pembacaan cepat tanpa biaya alokasi.
Jebakan: Memindahkan Pemilik Saat Dipinjam #
Sisi gelap pinjaman slice muncul saat kepemilikan dipindahkan di tengah-tengah masa pinjaman. Kode berikut mencoba mengirim cart — yang potongannya sedang dipinjam expensive — ke variabel baru:
fn main() {
let cart = vec![12000, 45000, 8000];
let expensive = &cart[1..];
let moved_cart = cart;
println!("Item mahal: {:?}", expensive);
println!("Isi keranjang: {:?}", moved_cart);
}Pesan error kompilator:
error[E0505]: cannot move out of `cart` because it is borrowed
--> main.rs:5:22
|
2 | let cart = vec![12000, 45000, 8000];
| ---- binding `cart` declared here
3 | let expensive = &cart[1..];
| ---- borrow of `cart` occurs here
4 |
5 | let moved_cart = cart;
| ^^^^ move out of `cart` occurs here
6 |
7 | println!("Item mahal: {:?}", expensive);
| --------- borrow later used here
|
help: consider cloning the value if the performance cost is acceptable
|
3 | let expensive = &cart.clone()[1..];
| ++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0505`.Pesan E0505 memuat kronologi lengkapnya: cart dipinjam pada baris 3, dipindahkan pada baris 5, dan pinjamannya masih dipakai pada baris 7. Perpindahan kepemilikan Vec ke moved_cart berarti data pengelola lama tidak lagi sah — sementara expensive masih menunjuk buffer heap lama melalui slice. Jika kompilator mengizinkannya, expensive berpotensi menggantung begitu moved_cart dijatuhkan atau saat realokasi terjadi — kelas bahaya yang kita bahas di section Ownership dan Memory. Solusi paling sederhana tetap urutan: selesaikan dulu seluruh pemakaian expensive sebelum memindahkan cart.
Kompilator juga menyodorkan jalan keluar kedua: &cart.clone()[1..]. Dengan clone, salinan mandiri dibuat di heap, slice meminjam salinan itu, dan cart asli bebas dipindahkan. Usulan ini mahal — satu penyalinan penuh data — namun sah ketika dua pihak memang butuh salinan independen.
Tabel Keputusan #
Kita padatkan seluruh pedoman section ini menjadi satu tabel rujukan cepat:
| Situasi | Pilihan | Alasan |
|---|---|---|
| Fungsi hanya membaca atau menghitung | &str / &[T] | pinjaman murah, pemanggil tidak kehilangan apa pun |
| Fungsi harus menerima banyak jenis wadah | &str / &[T] | satu tanda tangan menampung String, literal, array, Vec |
| Fungsi mengubah sebagian data di tempat | &mut [T] | hak tulis atas potongan tanpa perpindahan kepemilikan |
| Hasil fungsi dibangun dari nol | kembalikan String / Vec | data baru berdiri sendiri, tidak menumpang pinjaman |
| Data disimpan lebih lama dari pemanggil | terima kepemilikan utuh | pinjaman tidak boleh hidup melebihi pemiliknya |
| Dua salinan mandiri benar-benar dibutuhkan | clone | jalan keluar eksplisit dengan biaya penyalinan |
Baris pertama dan kedua adalah wilayah slice — mayoritas fungsi pembaca jatuh ke kategori ini. Baris ketiga memakai versi mutable dari slice yang kita coba di Chapter Array dan Vector Slice. Tiga baris terakhir adalah wilayah kepemilikan, dan clone selalu menjadi pilihan terakhir yang disadari biayanya.
Ringkasan Section Slice dan Referensi #
Section ini melengkapi kemampuan borrowing kita dengan dimensi pemotongan. Konsep slice menyatukan pointer dan panjang menjadi tampilan pinjaman &[T] yang tidak pernah menyalin data. String slice &str menerapkan konsep itu pada byte-byte teks — termasuk string literal yang sejak awal bertipe &str — sementara array dan Vec dipotong dengan keluarga notasi rentang yang sama: [a..b], [a..=b], [a..], [..b], dan [..]. Mutable slice &mut [T] membawa hak tulis ke dalam potongan, dan parameter slice menjadikan fungsi serbaguna lintas wadah berkat penyetaraan otomatis di titik pemanggilan. Seluruhnya tetap tunduk pada aturan borrowing: E0502 menjaga data dari perubahan di tengah pinjaman, E0505 menjaga pemilik dari perpindahan di tengah pinjaman, dan panic menjaga indeks agar tidak keluar dari jangkauan.
Dua perjalanan besar masih menanti. Section String dan Teks membahas teks Unicode secara utuh — karakter multi-byte, char boundary, dan cara memotong teks dengan aman. Section Lifetime membahas cara mendeskripsikan umur pinjaman secara eksplisit pada tanda tangan fungsi yang rumit — melengkapi penyederhanaan otomatis yang selama ini kita nikmati tanpa anotasi.