References Sebagai Cara Meminjam Value Pada Rust

Rust memberikan kita opsi untuk meminjam sebuah object atau value. Hal ini menjanjikan kebebasan dan mengurangi kompleksitas move dimana nilai tersebut masih dapat kita gunakan.

  1. Pendahuluan
  2. Aturan-aturan Penggunaan Reference
    1. Menambahkan Nilai Pada Value Pinjaman
    2. Peminjaman Value Multi pengguna
      1. Mutable Reference
        1. Solusi Permasalahan
      2. Immutable Reference
        1. Solusi permasalahan
    3. Reference yang Menggantung
  3. Penutup

Pendahuluan

Pada artikel mengenai manajemen memori dengan ownership kita diperkenalkan dengan move untuk memidahkan suatu kepemilikan. Hal itu rasanya tidak mencakup semua kebutuhan karena ada masanya kita tidak ingin melakukan move, cukup meminjam value saja.

Rust menyediakan references dengan syntax “&” yang dapat kita gunakan untuk meminjam suatu nilai tetapi tidak dengan hak kepemilikan atas value tersebut. Perhatikan contoh 1.0.

fn main() {
    let x: String = String::from("Hello word");
    let y: usize = kalkulasi_panjang(&x);
    println!("{} memiliki panjang {}", x, y);
}

fn kalkulasi_panjang(len: &String) -> usize {
    len.len()
}

Contoh 1.0

Pada kode di atas kita menggunakan value dari variabel x berupa String untuk dipinjamkan pada variabel y dengan penambahan syntax reference “&“.

Penting untuk diperhatikan bahwa value yang dipinjam bukan String tetapi &String. Jika menggunakan cara seperti biasa akan error karena tipe data tidak cocok.

Penggunaan reference tidak akan memindahkan ownership seperti move sehingga kita dapat menggunakan kembali variabel x seperti pada perintah println!(“{ } memiliki panjang { }”, x, y);.

Output dari kode di atas adalah sebagai berikut:

Hello word memiliki panjang 10

Contoh 1.1

Aturan-aturan Penggunaan Reference

Menambahkan Nilai Pada Value Pinjaman

fn main() {
    let mut x: String = String::from("Foo");
    add_on(&mut x);
    print!("{}", x);
}
fn add_on(adder: &mut String) {
    adder.push_str("bar");
    
}

Contoh 1.2

Kita dapat menambahkan nilai pada value yang dipinjam dengan menggunakan mut dan dibarengi &mut String. Saat kita menggunakan parameter pada fn add_on, yaitu (adder: &mut String) akan mengembalikan suatu nilai ketika dipanggil.

Perhaps they let you borrow it on the condition that you do not write in it or damage it in anyway. That would be an immutable borrow of the book.

Perhaps they let you borrow it with permission to write it it or mark it up some how. Say you are proof reading it and want to mark up mistakes. That would be a mutable borrow of the book.

Why think differently about Rust values?

Zi Cog – Forum Rust

Output dari kode di atas adalah sebagai berikut. Apakah terdapat sebuah perubahan pada variabel x?

Foobar

Contoh 1.3

Peminjaman Value Multi pengguna

Pada satu waktu mungkin saja kita dihadapkan terhadap kondisi meminjam 1 value yang sama untuk 2 variabel. Oleh karena itu, terdapat beberapa batasan dan aturan untuk menghindarkan dari masalah seperti data races.

Mutable Reference

fn main() {
    let mut x: String = String::from("Foobar");
    let y: &mut String = &mut x; 

    println!("{}", y);

    let z: &mut String = &mut x;

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

Contoh 1.3

Rust memiiliki aturan dimana kita tidak bisa meminjam value yang telah dipinjam untuk mutable reference. Kecuali peminjam pertama telah menggunakan value tersebut misalnya dengan perintah println!. Perhatikan line 14 ketika kita menghapus println!(“{}”, y); dan menggantinya dengan println!(“{} {}”, z, y);.

error[E0499]: cannot borrow `x` as mutable more than once at a time
 --> src/main.rs:4:26
  |
3 |     let y: &mut String = &mut x;     
  |                          ------ first mutable borrow occurs here
4 |     let z: &mut String = &mut x;
  |                          ^^^^^^ second mutable borrow occurs here
5 |     println!("{} {}", z, y);
  |                          - first borrow later used here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `ownership` (bin "ownership") due to previous error

Contoh 1.4

Hal ini terjadi untuk mencegah data race saat berada dalam compile time yang dapat menyebabkan masalah. Sebuah perlombaan dimana peminjam mencoba mengakses data aktual. Kondisi tersebut dapat terjadi ketika:

  • Pointer mengamil data yang sama pada waktu yang sama.
  • Salah satu pointer tersebut sedang digunakan untuk menulis data.
  • Tidak ada sinkronisasi untuk mengakses data.

Solusi Permasalahan

Jika kita tetap ingin menggunakan data yang sama untuk dua mutable refence maka gunakan “{}“. Perhatikan contoh 1.4 dibawah ini.

fn main() {
    let mut x: String = String::from("Foobar");
    {let y: &mut String = &mut x;
    } 
    let z: &mut String = &mut x;
}

Contoh 1.5

Opsi di atas dapat menghindarkan kita dari data race karena y akan terlebih dahulu diluar jangkauan (drop). Variabel y dapat digunakan dalam {} saja.

Immutable Reference

fn main(){
    let x: String = String::from("foo");
    let y: &String = &mut x;
    let z: &String = &x;

    println!("{} {} {}", x, y, z);
}

Contoh 1.6

Reference tersebut bersifat imutable yang artinya tidak akan ada perubahan pada value. Kita dapat menggunakan lebih dari satu reference mutable. Rust memiliki aturan dimana kita tidak bisa meminjam value yang sama menggunakan reference mutable dan imutable.

error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
  --> src/main.rs:8:22
   |
6  |     let y: &String = &mut x;
   |                      ------ mutable borrow occurs here
7  |
8  |     let z: &String = &x;
   |                      ^^ immutable borrow occurs here
...
11 |     println!("{} {}", y, z);
   |                       - mutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` (bin "ownership") due to previous error

Contoh 1.7

Contoh di atas menunjukan error dimana terdapat variabe y mutable dan x imutable meminjam value yang sama, yaitu x. Peminjaman ini akan berisiko karena pengguna mutable dapat merubah value sehingga pengguna immutable berpeluang membaca value yang telah berubah.

Solusi permasalahan

fn main(){
    let mut x: String = String::from("foo");

    {let y: &String = &mut x;
    println!("{}", y);}
    
    let z: &String = &x;
   
    println!("{}", z);
}

Contoh 1.8

Kode di atas menjadikan kita dapat menggunakan reference mutable dan immutable secara bersamaan. Kuncinya ada pada kurung kurawal { } atau memanggil variabel dengan println!, kita dapat menggunakan salah satu atau keduanya.

Pada saat kita memanggil variabel println!(“{ }”, y);} maka y secara otomatis telah digunakan. Jangkauan sebuah reference adalah terkahir kali dimana dilakukan pemanggilan atau “}“. Setelah terdapat println!(“{}”, y);} kita dapat membuat reference baru baik mutable atau immutable.

Reference yang Menggantung

fn main() {
    let x: &String = refot();
    
    println!("{}", x);
}

fn refot() -> &String { 
    let y: String = String::from("Foobar");
    &y
}

Contoh 1.9

Kode di atas menunjukan sebuah reference yang mengantung dimana fn refot akan mengembalikan sebuah nilai pinjaman dari y yang telah dibersihkan dari memori pada line 14. Rust tidak mengizinkan peminjaman mengantung sehingga terjadilah error.

error[E0106]: missing lifetime specifier
 --> src/main.rs:6:15
  |
6 | fn refot() -> &String { 
  |               ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
  |
6 | fn refot() -> &'static String { 
  |                +++++++

Contoh 2.0

Semuanya terlihat dengan jelas ketika compiler mengatakan:

help: this function’s return type contains a borrowed value, but there is no value for it to be borrowed from

Intinya adalah kita mencoba meminjam sesuatu yang tidak ada. Maka dari itu sebaiknya kita tidak menggunakan reference untuk kode di atas melainkan move. &String > String dan &y > y.

Penutup

Mutable reference memiliki sifat yang berbeda dengan immutable reference. Oleh karena itu rust memberikan batasan agar tidak terjadi masalah. Rust mengizinkan lebih dari satu immutable reference tetapi tidak dengan mutable reference atau campuran keduanya.

Lambat laun saya mulai sadar bahwa yang menjadikan perubahan terbesar dalam hidup adalah konsistensi terhadap suatu hal.

Qori Ilmanudin

Discover more from Qorinotes

Subscribe to get the latest posts sent to your email.

Qori Avatar

Published by

Categories: