Rust — Berkenalan dengan si Paling dicintai
Menurut survei stackoverflow pada tahun 2021 tentang bahasa pemrograman yang paling dicintai jatuh kepada teman kita yang bernama Rust. Yap, dari data survei yang sama sekitar 86.98% dari 82.914 responden mengaku Rust merupakan bahasa yang mereka sukai. Hal ini tentu saja tidak terlepas dari apa yang dapat ditawarkan oleh Rust itu sendiri, diantaranya keamanan memory, ownership memory management, type system dan banyak lagi tentunya.
Setelah sedikit mengulik tentang rust maka saya sedikit tertarik untuk membahas tentang ownership memory management.
Ada beberapa konsep manajemen memory yang mungkin sudah kita ketahui yang terdapat dalam bahasa pemrograman diantaranya adalah Garbace Collection (GC), Automatic Reference Counting (ARC), Manual Memory Management dan Ownership yang terdapat pada rust.
Apa itu Ownership Memory Management ?
“Ownership is a set of rules that govern how a Rust program manages memory. All programs have to manage the way they use a computer’s memory while running.”
Dikutip dari dokumentasi rust itu sendiri menjelaskan bahwa ownership merupakan sekumpulan aturan-aturan tentang bagaimana program mengelola memori. Aturan-aturan ini tentu menjadi rujukan bagi programmer dalam menulis kode program.
Ownership Rules
Berikut merupakan aturan dalam mengelola memori dalam rust, dan aturan ini harus diingat dan diterapkan dengan sebaik mungkin:
- Setiap nilai atau data di rust memiliki sebuah owner (permilik).
- Hanya boleh ada satu owner untuk satu data di saat yang bersamaan. Saat yang bersamaan di sini dimaksud adalah waktu eksekusi sebuah scope dari kode program.
- Ketika eksekusi dari owner pada sebuah block scope selesai, maka data yang ada dalam scope tersebut akan di-dealokasikan.
Variable Scope
Sebelum membahas aturan tentang ownership maka lebih baik kita bahas tentang variable scope.
Variable scope sendiri dapat dianggap sebagai cakupan atau batasan di mana sebuah variabel dianggap valid.
{ // name belum dideklarasikan, name tidak valid
let name = "Khai"; // name dideklarasikan, name valid
{
println!("{}", name); // name valid
}
println!("{}", name); // name tetap valid
} // scope berakhir, setelah baris ini name tidak akan valid
Dapat disimpulkan bahwa :
- saat name mulai dideklarasikan di sebuah scope, maka itu akan valid.
- variabel name akan tetap valid sampai scope nya berakhir.
Pada contoh kasus di atas hanya terdapat satu deklarasi variabel dengan nama name dan nilai Khai. Bagaimana jika terdapat beberapa variabel yang sama dengan tipe data yang berbeda atau sama dalam satu scope ?
Shadowing
Jika terdapat dua atau lebih variabel yang dengan nama yang persis sama dalam satu scope maka dapat dikatakan bahwa itu adalah sebuah shadowing.
{
let my_variable = "var value";
println!("{}", my_variable);
let my_variable = 1;
println!("{}", my_variable);
let my_variable = false;
println!("{}", my_variable);
}
Perlu diketahui bahwa meskipun memiliki variabel dengan nama yang sama persis rust akan menganggap semua variabel tersebut adalah variabel yang berbeda.
Copy and move semantics
Saat kita membuat sebuah program tentu menjadi hal yang lazim jika sebuah variabel akan menjadi nilai variabel yang lain.
{
let first_variable = 17;
let second_variable = first_variable;
println!("{}", second_variable);
}
Dapat dilihat bahwa first_variable memilik nilai 17, di mana ini berarti first_variable merupakan owner dari nilai 17. Lalu bagaimana dengan dengan second_variable?
Aturan pertaman dari konsep ownership adalah setiap nilai atau data hanya boleh memiliki satu owner maka terdapat dua jenis prilaku saat kita menjadikan sebuah variabel sebagai nilai dari variabel lainnya.
Pertama, semua variabel primitif menganut prilaku copy semantic yaitu saat first_variable dijadikan nilai dari second_variable maka second_variable akan meng-copy data dari first_variable dan menjadikan second_variable sebagai owner baru untuk data yang di-copy tersebut.
Kedua, prilaku move semantic, untuk lebih memahami prilaku ini mari kita lihat contoh di bawah.
{
let first_variable = String::from("First Variable");
let second_variable = first_variable;
println!("{}", first_variable);
}
Pada contoh di atas terlihat bawah kita membuat first_variable dengan tipe data String memiliki nilai First Variable, lalu kemudian kita menjadikan first_variable menjadi nilai dari second_variable, di baris selanjutnya kita inging mencetak nilai dari first_variable. Apa yang akan terjadi ?
Jika program di atas dijalankan, maka akan muncul error
error[E0382]: borrow of moved value: `first_variable`
Mengapa demikian, ini karena saat kita menjadikan first_variable sebagai nilai dari second_variable nilai beserta ownership dari first_variable akan ikut berpindah ke second_variable, prilaku ini disebut sebagai move semantic. Bagaimana jika kita tetap ingin menjadikan first_variable sebagai nilai dari second_variable namun first_variable tetap dapat dicetak tanpa error ?
Borrowing
Untuk menyelesaikan masalah pada bahasan di atas maka kita perlu berkenalan dengan istilah Borrowing, istilah ini sendiri bila diterjemahkan ke dalam bahasa indonesia berarti peminjaman. Seperti namanya konsep borrowing akan meminjam atau merefrensikan nilai variabel ke nilai variabel yang lain. Caranya adalah dengan menambahkan operator “&” di depan variabel yang di jadikan nilai variabel lain.
{
let first_variable = String::from("First Variable");
let second_variable = &first_variable; // operasi borrow (peminjaman)
println!("{}", first_variable);
}
Dengan menambahkan “&” di depan first_variable saat di-assign ke second_variable maka second_variable akan merujuk pada pointer si first_variable sehingga owner dari data tersebut tetaplah first_variable atau dengan istilah lebih sederhananya second_variable hanya meminjam nilai dari first_variable untuk sementara waktu.
Sekian saja mungkin perkenalan dengan bahasa pemrograman yang paling dicintai menurut stactoverflow ini. Sebagai catatan semua variabel pada contoh di atas merupakan bentuk immutable untuk yang mutable mungkin akan dibahas lain kali atau bisa dilihat dari berbagai sumber lain :)