Fungsi adalah tempat pertemuan antar bagian program, sekaligus tempat move paling sering terjadi di kode nyata. Setiap kali kita mengirim nilai ke dalam fungsi atau menerima nilai keluar dari fungsi, kepemilikan ikut berpindah — dan pemanggilan fungsi yang tampak biasa di bahasa lain bisa menghasilkan error kompilasi di Rust. Pada Chapter ini kita akan menelusuri dua arah perpindahan tersebut: masuk lewat parameter dan keluar lewat nilai kembalian, lengkap dengan pesan error aslinya. Pemahaman dua arah ini juga akan menyiapkan kita mengenal borrowing sebagai jalan tengah yang jauh lebih nyaman.

Memindahkan Kepemilikan ke Dalam Fungsi #

Aturannya sederhana: mengirim nilai ke fungsi sama seperti melakukan assignment ke parameter. Untuk tipe Copy, nilai tersalin dan variabel pemanggil tetap sah. Untuk tipe non-Copy seperti String, kepemilikan berpindah ke parameter di dalam fungsi.

Mari kita mulai dengan contoh yang sehat — sebuah fungsi pembuat pesan sapaan:

rust
fn build_greeting(name: String) -> String {
    format!("Halo, {}! Selamat belajar Rust.", name)
}

fn main() {
    let username = String::from("Budi");
    let greeting = build_greeting(username);

    println!("{}", greeting);
}

Output yang Dihasilkan:

text
Halo, Budi! Selamat belajar Rust.

Mari kita telusuri perjalanan kepemilikannya baris demi baris:

  1. Baris 6 (let username = ...): username di dalam main menjadi pemilik teks "Budi" di heap.
  2. Baris 7 (let greeting = build_greeting(username);): pemanggilan ini terdiri dari dua kejadian. Pertama, kepemilikan teks "Budi" berpindah dari username ke parameter name milik build_greeting — sejak baris ini username tidak valid lagi di main. Kedua, format! membangun String baru, dan String baru tersebut di-return sehingga kepemilikannya berpindah ke greeting.
  3. Baris 1 (fn build_greeting(name: String) -> String): parameter name mengambil alih kepemilikan teks yang dikirim pemanggil. Nilai kembalian String akan berpindah lagi ke pemanggil.
  4. Baris 2 (format!(...)): format! menghasilkan String baru di heap. Setelah fungsi selesai, parameter name keluar scope dan di-drop — teks "Budi" asli pun dipulangkan ke allocator di titik ini, bukan saat main berakhir.
  5. Baris 9 (println!("{}", greeting);): greeting sah dipakai karena kepemilikannya diterima lewat nilai kembalian. Sebaliknya, username sudah tidak bisa disentuh lagi.

Perhatikan bahwa kehidupan teks "Budi" dan teks sapaan berjalan terpisah: teks pertama lahir di main, pindah ke name, lalu mati bersama akhir build_greeting; teks kedua lahir di dalam fungsi, pindah ke greeting, dan mati di akhir main.

Kode yang Gagal: Variabel Setelah Dipindahkan ke Fungsi #

Sekarang kita lihat bentuk pelanggaran yang paling sering dialami pemula. Kode berikut mencoba memakai note setelah nilainya dikirim ke print_message — kode ini tidak dapat dikompilasi:

rust
fn print_message(message: String) {
    println!("Pesan: {}", message);
}

fn main() {
    let note = String::from("Belajar ownership hari ini");
    print_message(note);

    println!("Catatan: {}", note);
}

Kompilasi dengan rustc --edition 2021 main.rs akan gagal sebagai berikut.

Pesan error kompilator:

text
error[E0382]: borrow of moved value: `note`
 --> main.rs:9:29
  |
6 |     let note = String::from("Belajar ownership hari ini");
  |         ---- move occurs because `note` has type `String`, which does not implement the `Copy` trait
7 |     print_message(note);
  |                   ---- value moved here
8 |
9 |     println!("Catatan: {}", note);
  |                             ^^^^ value borrowed here after move
  |
note: consider changing this parameter type in function `print_message` to borrow instead if owning the value isn't necessary
 --> main.rs:1:27
  |
1 | fn print_message(message: String) {
  |    -------------          ^^^^^^ this parameter takes ownership of the value
  |    |
  |    in this function
help: consider cloning the value if the performance cost is acceptable
  |
7 |     print_message(note.clone());
  |                       ++++++++

error: aborting due to 1 previous error

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

Pesan E0382 di atas memuat anotasi yang sangat membantu. Bagian value moved here pada baris 7 menunjuk pemanggilan print_message(note) sebagai titik perpindahan, dan bagian value borrowed here after move pada baris 9 menunjuk println! sebagai titik pelanggaran. Lebih menarik lagi, kompilator menyertakan note khusus: parameter message sebaiknya diubah menjadi bentuk pinjaman (borrow) karena fungsi ini sebenarnya hanya membaca nilai, bukan menggenggamnya. Saran tersebut adalah petunjuk menuju konsep referensi dan borrowing yang akan kita bahas tuntas pada Chapter 6.

Mengembalikan Kepemilikan dari Fungsi #

Arah kedua perpindahan adalah keluar dari fungsi. Setiap return — termasuk nilai di baris terakhir tanpa kata kunci return — memindahkan kepemilikan nilai ke pemanggil. Pola ini berguna ketika fungsi perlu mengubah data heap dan menyerahkan hasil akhirnya kembali:

rust
fn add_degree(name: String) -> String {
    let mut full_name = name;
    full_name.push_str(", S.Kom");
    full_name
}

fn main() {
    let lecturer_name = String::from("Siti Rahma");
    let degree_name = add_degree(lecturer_name);

    println!("Nama dengan gelar: {}", degree_name);
}

Output yang Dihasilkan:

text
Nama dengan gelar: Siti Rahma, S.Kom

Mari kita bedah alur kepemilikannya:

  1. Baris 8 (let lecturer_name = ...): lecturer_name di main memiliki teks "Siti Rahma".
  2. Baris 9 (let degree_name = add_degree(lecturer_name);): kepemilikan teks berpindah ke parameter name; lecturer_name berhenti valid.
  3. Baris 2 (let mut full_name = name;): di dalam fungsi, kepemilikan pindah lagi dari name ke full_name. Perpindahan internal seperti ini sah karena name tidak pernah dipakai lagi setelah baris tersebut.
  4. Baris 3 (full_name.push_str(", S.Kom");): fungsi mengubah isi teks di heap menjadi "Siti Rahma, S.Kom". Kata kunci mut pada full_name diperlukan agar isinya boleh diubah.
  5. Baris 4 (full_name): baris terakhir tanpa titik koma adalah nilai kembalian. Kepemilikan berpindah keluar dari fungsi menuju variabel degree_name di main, dan fungsi tidak menjatuhkan full_name karena nilai tersebut sudah bukan miliknya.

Fungsi dengan tanda tangan String -> String seperti ini bekerja dengan sempurna — data masuk, diubah, lalu keluar kembali. Pertanyaannya: nyamankah pola ini untuk semua kebutuhan?

Pola Ambil dan Kembalikan, serta Harganya #

Bayangkan sebuah fungsi yang hanya perlu membaca panjang sebuah teks. Mengikuti pola sejauh ini, fungsinya harus menerima String dan mengembalikan String kembali agar pemanggil tetap bisa memakai teksnya — sebuah ritual bolak-balik hanya untuk membaca satu angka. Setiap fungsi pembaca akan menuntut ritual serupa, dan kode kita cepat terbebani variabel penampung serta nilai kembalian yang hilir mudik tanpa makna.

Pola ambil-dan-kembalikan memang sah dan kadang memang tepat — misalnya pada add_degree yang benar-benar mengubah data. Namun untuk operasi baca-tulis ringan, Rust menyediakan mekanisme yang jauh lebih ringkas: meminjam nilai lewat reference tanpa memindahkan kepemilikan sama sekali.

Ringkasan #

Parameter fungsi menerima kepemilikan lewat move (untuk tipe non-Copy) atau salinan murah (untuk tipe Copy), sementara nilai kembalian mengalihkan kepemilikan ke pemanggil. Kita telah membaca pesan E0382 versi fungsi, termasuk saran kompilator untuk mengubah parameter menjadi pinjaman. Dua saran itu — clone dan borrow — adalah dua jalan keluar resmi dari kebuntuan ownership, dan jalan yang kedua adalah bintang utama section ini: referensi dan borrowing.