Halo teman-teman semuanya, pada artikel sebelumnya kita sudah membahas tentang fungsi di Rust, mulai dari fungsi sederhana, fungsi dengan parameter, return value, hingga penggunaan keyword return
. Kali ini kita akan masuk ke konsep yang menjadi ciri khas Rust dan sering dianggap menakutkan di awal, yaitu ownership.
Ownership adalah sistem yang digunakan Rust untuk mengatur bagaimana memori dialokasikan dan dibebaskan tanpa perlu garbage collector. Dengan ownership, Rust bisa menjaga program tetap aman dari error memori seperti dangling pointer, use after free, atau double free yang sering terjadi di bahasa lain.
Walaupun terlihat rumit, ownership sebenarnya mengikuti aturan yang jelas dan sederhana. Jika kita memahami aturan dasarnya, maka menulis program Rust akan terasa lebih mudah. Mari kita bahas aturan ownership satu per satu.
Aturan Dasar Ownership
Ada tiga aturan utama dalam ownership di Rust:
- Setiap nilai di Rust dimiliki oleh sebuah variabel.
- Hanya ada satu pemilik (owner) pada suatu waktu.
- Ketika owner keluar dari scope, nilai tersebut akan di-drop (memori dibebaskan).
Contoh Ownership Dasar
fn main() {
let s1 = String::from("hello");
let s2 = s1; // ownership berpindah ke s2
println!("{}", s2);
// println!("{}", s1); // error: s1 sudah tidak valid
}
Pada contoh di atas, variabel s1
memiliki string "hello"
. Saat kita menugaskan s1
ke s2
, ownership berpindah dari s1
ke s2
. Akibatnya, s1
tidak bisa lagi digunakan. Jika kita mencoba mengakses s1
, program tidak akan bisa di-compile.
Hal ini berbeda dengan tipe data copy (seperti integer), yang masih bisa digunakan setelah assignment.
Tipe Data Copy
Tipe data sederhana seperti integer, boolean, dan float memiliki trait Copy
, sehingga mereka tidak mengikuti aturan move, melainkan di-copy saat assignment.
fn main() {
let x = 10;
let y = x; // nilai x di-copy, bukan dipindah
println!("x: {}, y: {}", x, y);
}
Karena i32
adalah tipe yang mendukung copy, variabel x
masih bisa digunakan setelah nilainya ditugaskan ke y
.
Ownership dalam Fungsi
Ownership juga berlaku saat variabel dikirim ke fungsi.
fn main() {
let s = String::from("rust");
ambil_ownership(s);
// println!("{}", s); // error: s sudah tidak valid
}
fn ambil_ownership(str: String) {
println!("String: {}", str);
}
Variabel s
dipindahkan ke fungsi ambil_ownership
, sehingga ownership berpindah. Setelah itu, s
tidak bisa lagi digunakan di fungsi main
.
Sementara untuk tipe data copy, variabel masih bisa digunakan setelah dipanggil ke fungsi.
fn main() {
let n = 5;
cetak(n);
println!("n masih bisa dipakai: {}", n);
}
fn cetak(x: i32) {
println!("Nilai: {}", x);
}
Ownership dan Return Value
Fungsi juga bisa mengembalikan ownership ke pemanggilnya.
fn main() {
let s1 = kembalikan();
println!("Hasil: {}", s1);
}
fn kembalikan() -> String {
let str = String::from("Rustacean");
str // ownership berpindah ke pemanggil
}
Di sini, ownership str
dikembalikan ke variabel s1
di main
. Dengan begitu, variabel tetap bisa digunakan setelah fungsi selesai.
Kesimpulan
Pada artikel ini kita sudah belajar konsep ownership di Rust, aturan dasarnya, bagaimana ownership berpindah saat assignment, bagaimana tipe data sederhana berbeda karena sifat copy, bagaimana ownership bekerja saat variabel masuk ke fungsi, dan bagaimana ownership bisa dikembalikan lewat return value.
Ownership mungkin terasa aneh di awal, tetapi inilah yang membuat Rust aman sekaligus efisien tanpa garbage collector. Pada artikel berikutnya, kita akan membahas tentang Borrowing & References, yaitu cara meminjam data tanpa memindahkan ownership.
Terima Kasih