Program yang menghitung total belanja, mengelola stok gudang, atau melacak saldo pengguna bekerja dengan pola yang sama: menyimpan sebuah nilai ke dalam nama yang mudah diingat, lalu membaca kembali nilai tersebut saat dibutuhkan. Rust menyediakan mekanisme variable untuk keperluan tersebut, tetapi dengan satu keputusan desain yang mengejutkan bagi kita yang datang dari PHP atau JavaScript: secara bawaan, variable di Rust bersifat immutable atau tidak dapat diubah setelah diberi nilai. Pada Chapter ini kita akan membuat variable dengan kata kunci let, mengalami langsung galat kompilasi ketika mencoba mengubah variable immutable, lalu memperbaikinya dengan kata kunci mut. Pada akhir chapter kita juga akan memahami alasan Rust memilih keamanan sebagai jalur bawaan.

Mengikat Nilai dengan let #

Variable di Rust adalah pengenal (identifier) yang mengikat sebuah nama ke sebuah nilai. Kata kunci let memberi tahu kompilator untuk menyiapkan tempat bagi nilai tersebut, menentukan tipenya, dan mendaftarkan nama variable di dalam scope saat ini. Berbeda dengan PHP, penulisan nama variable di Rust tidak diawali tanda $, dan setiap statement wajib diakhiri titik koma.

Kita mulai dari contoh paling sederhana, yaitu menghitung total belanja:

rust
fn main() {
    let harga = 50000;
    let jumlah = 3;
    let total = harga * jumlah;

    println!("Harga satuan: Rp{}", harga);
    println!("Jumlah: {}", jumlah);
    println!("Total: Rp{}", total);
}

Output yang Dihasilkan:

text
Harga satuan: Rp50000
Jumlah: 3
Total: Rp150000

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 2 (let harga = 50000;): kata kunci let membuat variable bernama harga dan mengikatnya ke nilai integer 50000; kompilator menentukan tipe harga sebagai i32, yaitu tipe bawaan untuk bilangan bulat.
  2. Baris 3 (let jumlah = 3;): variable jumlah dibuat dengan nilai 3 menggunakan pola yang sama.
  3. Baris 4 (let total = harga * jumlah;): kompilator membaca nilai dari harga dan jumlah, mengalikan keduanya menjadi 150000, lalu mengikat hasilnya ke variable baru bernama total.
  4. Baris 6-8 (println!(...)): ketiga baris mencetak nilai setiap variable ke terminal; tanda kurung kurawal {} berfungsi sebagai slot yang diganti oleh nilai variable di belakang tanda koma.

Perhatikan bahwa program di atas membaca harga dua kali, yaitu pada baris 4 dan baris 6. Membaca nilai berulang kali tidak masalah, karena yang dilarang bukan membaca, melainkan menimpa nilai tanpa izin.

Variable Rust Bersifat Immutable secara Bawaan #

Masalah nyata muncul ketika data memang perlu berubah, misalnya stok gudang berkurang setiap kali barang terjual. Insting kita mungkin menulis stok = stok - 4; seperti pola di PHP. Mari kita coba langsung dan lihat reaksi kompilator:

rust
fn main() {
    let stok = 10;
    println!("Stok awal: {}", stok);

    stok = stok - 4;
    println!("Stok akhir: {}", stok);
}

Kode tersebut gagal dikompilasi. Pesan galat lengkapnya berbunyi:

text
error[E0384]: cannot assign twice to immutable variable `stok`
 --> main.rs:5:5
  |
2 |     let stok = 10;
  |         ---- first assignment to `stok`
...
5 |     stok = stok - 4;
  |     ^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
  |
help: consider making this binding mutable
  |
2 |     let mut stok = 10;
  |         +++

error: aborting due to 1 previous error

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

Galat E0384 menyebutkan dua lokasi sekaligus: baris ke-2 tempat stok pertama kali diberi nilai, dan baris ke-5 tempat kita mencoba memberi nilai kedua. Kompilator bahkan memberikan saran perbaikan berupa penambahan kata kunci mut pada deklarasi awal. Perlu kita garis bawahi bahwa galat ini terjadi sebelum program sempat berjalan; Rust menolak menghasilkan program biner dari kode yang melanggar aturan mutabilitas.

Membuat Variable Mutable dengan mut #

Ketika sebuah nilai memang dirancang berubah, kita menyatakannya secara eksplisit dengan menambahkan kata kunci mut di antara let dan nama variable. Kata kunci ini berlaku sejak deklarasi, bukan ditambahkan di tengah program. Berikut perbaikan dari kode sebelumnya:

rust
fn main() {
    let mut stok = 10;
    println!("Stok awal: {}", stok);

    stok = stok - 4;
    println!("Stok akhir: {}", stok);
}

Output yang Dihasilkan:

text
Stok awal: 10
Stok akhir: 6

Mari kita bedah perubahan pentingnya:

  1. Baris 2 (let mut stok = 10;): kata kunci mut memberi izin kepada variable stok untuk diberi nilai baru di kemudian hari; nilai awalnya tetap 10.
  2. Baris 5 (stok = stok - 4;): baris ini sekarang sah; kompilator membaca nilai lama stok yaitu 10, menguranginya dengan 4, lalu menimpa isi stok dengan hasil 6.
  3. Baris 6 (println!("Stok akhir: {}", stok);): pencetakan menunjukkan isi stok yang terbaru, sehingga tercetak 6.

Kata kunci mut hanya mengizinkan penimpaan nilai dengan tipe yang sama. Kita boleh menulis stok = 8; karena 8 juga bilangan bulat, tetapi menulis stok = 8.5; akan ditolak karena tipe berubah menjadi bilangan desimal. Teknik mengubah nilai sekaligus tipenya akan kita pelajari pada Chapter Shadowing Variabel.

Sebagai bukti, program berikut memberi nilai awal lalu hanya membaca stok tanpa pernah menimpanya:

rust
fn main() {
    let mut stok = 10;
    println!("Stok: {}", stok);
}

Program tetap berjalan dan mencetak Stok: 10, tetapi kompilator menampilkan peringatan berikut:

text
warning: variable does not need to be mutable
 --> main.rs:2:9
  |
2 |     let mut stok = 10;
  |         ----^^^^
  |         |
  |         help: remove this `mut`
  |
  = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default

warning: 1 warning emitted

Mengapa Rust Memilih Keamanan #

Sebagian besar kesalahan pemrograman yang sulit dilacak berasal dari data yang berubah tanpa disadari. Sebuah variable yang diubah di tempat jauh dari tempat pembacaannya memaksa kita menelusuri kode secara manual hanya untuk mengetahui nilai terkini. Dengan immutable sebagai bawaan, kompilator menjamin bahwa variable yang kita baca masih memegang nilai yang sama sejak dideklarasikan, kecuali deklarasinya secara eksplisit mengandung mut.

Keputusan ini juga berbuah pada program yang menjalankan banyak thread. Thread lain yang membaca variable immutable tidak perlu khawatir nilainya diganti di tengah jalan, sehingga banyak kelas galat concurrency berkurang bahkan sebelum program berjalan. Terakhir, penandaan mut membuat niat kode terbaca jelas: setiap kemunculan mut adalah titik yang layak kita periksa saat melakukan code review atas logika bisnis.

Ringkasan #

Kata kunci let mengikat nama ke nilai, dan variable seperti itu bersifat immutable secara bawaan. Upaya menimpa variable immutable digagalkan kompilator lewat galat E0384, lengkap dengan saran penambahan mut. Kata kunci mut dinyatakan saat deklarasi dan hanya mengizinkan penimpaan nilai bertipe sama. Dengan fondasi ini, kita siap mengenal tipe-tipe data yang dapat disimpan ke dalam variable pada Chapter Tipe Data Skalair.