Pendahuluan

Saat mulai belajar Rust, salah satu hal pertama yang terasa berbeda dibanding banyak bahasa lain adalah cara Rust memperlakukan variabel. Di Rust, variabel secara default bersifat immutable, artinya nilainya tidak bisa diubah setelah diberikan. Keputusan desain ini bukan sekadar gaya sintaks, tetapi bagian penting dari pendekatan Rust terhadap keamanan memori, prediktabilitas program, dan pencegahan bug sejak tahap kompilasi.

Selain memahami mutabilitas, pengembang juga perlu mengenal tipe data dasar yang paling sering dipakai: integer, floating-point, boolean, char, dan string sederhana. Kombinasi antara mutabilitas yang eksplisit dan sistem tipe yang jelas membuat kode Rust cenderung lebih aman dan lebih mudah dianalisis oleh compiler.

Artikel ini membahas konsep-konsep tersebut dari sudut pandang praktis. Kita akan melihat bagaimana mendeklarasikan variabel, kapan memakai mut, bagaimana anotasi tipe digunakan, dan apa saja tipe data dasar yang perlu dipahami di awal.

Variabel di Rust: Immutable Secara Default

Deklarasi variabel paling dasar di Rust menggunakan kata kunci let. Contoh:

fn main() {
    let nama = "Ayu";
    let umur = 28;

    println!("Nama: {}, umur: {}", nama, umur);
}

Pada contoh di atas, nama dan umur tidak bisa diubah setelah diberi nilai. Jika Anda mencoba menetapkan nilai baru ke variabel tersebut, compiler akan menolak kode itu.

fn main() {
    let umur = 28;
    umur = 29; // error
}

Rust akan menghasilkan error kompilasi karena umur tidak dideklarasikan sebagai mutable. Ini adalah salah satu ciri utama Rust: perubahan state harus dilakukan secara eksplisit.

Mengapa Rust Membuat Variabel Immutable Secara Default?

Ada beberapa alasan teknis yang kuat:

  • Mengurangi bug akibat perubahan state yang tidak disengaja. Jika nilai tidak bisa berubah, alur program lebih mudah dipahami.
  • Membantu compiler melakukan analisis yang lebih aman. Rust sangat mengandalkan pemeriksaan saat kompilasi untuk mencegah masalah runtime.
  • Mendorong desain yang lebih jelas. Jika suatu nilai memang akan berubah, programmer harus menyatakannya dengan eksplisit.
  • Mempermudah pemeliharaan kode. Saat membaca kode orang lain, variabel immutable memberi sinyal bahwa nilai tersebut stabil.

Pendekatan ini berguna terutama pada codebase yang besar. Semakin banyak tempat yang dapat mengubah nilai, semakin sulit melacak sumber bug. Dengan immutable sebagai default, Rust membuat perubahan menjadi sesuatu yang sadar dan terlihat jelas.

Catatan: Immutable bukan berarti Rust kaku. Rust tetap mendukung perubahan nilai, tetapi harus dinyatakan secara eksplisit dengan mut.

Menggunakan Variabel Mutable dengan mut

Jika Anda memang perlu mengubah nilai variabel, gunakan kata kunci mut.

fn main() {
    let mut counter = 0;

    counter = counter + 1;
    counter = counter + 1;

    println!("Nilai counter: {}", counter);
}

Pada contoh ini, counter boleh diubah karena dideklarasikan sebagai let mut. Hasilnya akan mencetak nilai 2.

Kapan Sebaiknya Menggunakan mut?

Gunakan mut ketika variabel benar-benar merepresentasikan state yang berubah, misalnya:

  • penghitung dalam loop,
  • akumulator untuk hasil perhitungan,
  • buffer yang diisi bertahap,
  • nilai konfigurasi yang dimodifikasi sebelum dipakai.

Namun, jangan terlalu cepat memakai mut untuk semua variabel. Praktik yang baik di Rust adalah menjaga variabel tetap immutable selama memungkinkan. Selain lebih aman, ini juga membantu pembaca kode memahami bagian mana yang benar-benar mengalami perubahan state.

Kesalahan Umum Pemula

Salah satu kesalahan yang sering terjadi adalah lupa menambahkan mut lalu bingung karena compiler menolak assignment baru. Contohnya:

fn main() {
    let total = 10;
    total = 15;
}

Perbaikannya sederhana:

fn main() {
    let mut total = 10;
    total = 15;

    println!("Total: {}", total);
}

Compiler Rust biasanya memberikan pesan error yang cukup jelas. Biasakan membaca pesan tersebut dengan teliti karena sering kali compiler juga memberi saran perbaikan.

Anotasi Tipe dan Inferensi Tipe

Rust adalah bahasa dengan sistem tipe statis. Artinya, tipe setiap nilai diketahui saat kompilasi. Namun, Anda tidak selalu harus menuliskan tipe secara eksplisit karena Rust mampu melakukan type inference.

fn main() {
    let jumlah = 100;
    let harga = 19.5;
    let aktif = true;
}

Di sini, compiler bisa menebak bahwa jumlah adalah integer, harga adalah float, dan aktif adalah boolean.

Meski demikian, anotasi tipe tetap penting dalam beberapa situasi:

  • saat ingin memperjelas maksud kode,
  • saat ada ambiguitas,
  • saat bekerja dengan input, parsing, atau API tertentu,
  • saat ingin mengontrol ukuran atau presisi tipe numerik.

Contoh anotasi tipe:

fn main() {
    let jumlah: i32 = 100;
    let harga: f64 = 19.5;
    let aktif: bool = true;
    let inisial: char = 'R';
}

Menulis anotasi tipe seperti ini dapat membantu dokumentasi kode, terutama ketika pembaca perlu tahu tipe yang dipilih tanpa harus menebaknya dari konteks.

Tipe Data Dasar di Rust

1. Integer

Tipe integer digunakan untuk bilangan bulat, baik positif maupun negatif. Rust menyediakan beberapa ukuran integer, misalnya i8, i16, i32, i64, dan i128 untuk signed integer, serta u8, u16, u32, u64, dan u128 untuk unsigned integer.

fn main() {
    let suhu: i32 = -5;
    let jumlah_pengguna: u32 = 1500;

    println!("Suhu: {}, pengguna: {}", suhu, jumlah_pengguna);
}

Jika Anda tidak menuliskan anotasi tipe, Rust umumnya menganggap literal integer sebagai i32 dalam banyak konteks dasar. i32 adalah pilihan default yang umum dan cukup efisien untuk banyak kebutuhan aplikasi.

Tips praktis: pilih tipe integer berdasarkan domain data. Misalnya, jumlah item yang tidak mungkin negatif sering cocok menggunakan unsigned integer, tetapi banyak pengembang tetap memilih signed integer demi konsistensi dan kemudahan interoperabilitas. Yang terpenting adalah memahami rentang nilainya.

2. Floating-Point

Tipe floating-point dipakai untuk bilangan desimal. Rust menyediakan dua tipe utama: f32 dan f64. Secara umum, f64 menjadi pilihan default karena presisinya lebih tinggi.

fn main() {
    let harga: f64 = 99.99;
    let rasio: f32 = 0.75;

    println!("Harga: {}, rasio: {}", harga, rasio);
}

Gunakan float saat memang perlu nilai pecahan. Namun, untuk kasus seperti perhitungan uang pada sistem produksi, float sering bukan pilihan terbaik karena masalah presisi representasi biner. Untuk tahap dasar pembelajaran, cukup pahami bahwa float cocok untuk data numerik desimal umum, tetapi perlu kehati-hatian pada perhitungan yang sangat sensitif.

3. Boolean

Tipe bool hanya memiliki dua nilai: true dan false. Tipe ini sering dipakai untuk kondisi logika, validasi, status aktif/nonaktif, atau hasil perbandingan.

fn main() {
    let is_login: bool = true;
    let is_admin = false;

    println!("Login: {}, Admin: {}", is_login, is_admin);
}

Boolean menjadi inti dari percabangan seperti if. Karena Rust memiliki sistem tipe yang ketat, Anda tidak bisa menggunakan integer sebagai pengganti boolean seperti yang kadang terjadi di bahasa lain.

fn main() {
    let aktif = true;

    if aktif {
        println!("Akun aktif");
    }
}

Aturan yang ketat ini membantu menghindari ambiguitas dan membuat niat kode lebih jelas.

4. Char

Tipe char di Rust merepresentasikan satu karakter Unicode dan ditulis dengan tanda petik tunggal.

fn main() {
    let huruf: char = 'A';
    let simbol: char = '✓';

    println!("{} {}", huruf, simbol);
}

Penting untuk memahami bahwa char bukan string satu karakter versi sederhana, melainkan satu nilai karakter Unicode. Ini berguna ketika Anda benar-benar bekerja dengan satu karakter individual.

Kesalahan umum adalah menukar penggunaan petik tunggal dan petik ganda:

  • 'A' adalah char
  • "A" adalah string

5. String Sederhana

Untuk pengenalan awal, ada dua bentuk string yang sering ditemui di Rust: string slice (&str) dan String. Untuk fondasi dasar, Anda cukup mulai dari &str, yaitu literal string yang sering ditulis langsung di kode sumber.

fn main() {
    let pesan: &str = "Halo, Rust!";
    println!("{}", pesan);
}

&str cocok untuk teks yang tetap dan tidak perlu diubah. Sementara itu, String digunakan untuk teks yang dimiliki program dan bisa bertambah atau berubah. Karena artikel ini fokus pada fondasi, cukup pahami dulu bahwa string di Rust tidak selalu satu tipe tunggal.

Contoh string yang dapat diubah:

fn main() {
    let mut nama = String::from("Budi");
    nama = String::from("Budi Santoso");

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

Perhatikan bahwa mutabilitas variabel dan sifat data adalah hal yang saling berkaitan tetapi berbeda. Variabel nama di atas mutable karena dideklarasikan dengan mut, sehingga ia bisa diberi nilai baru.

Contoh Gabungan: Variabel dan Tipe Dasar

Contoh berikut menggabungkan beberapa konsep dasar dalam satu program kecil:

fn main() {
    let id: u32 = 1001;
    let mut stok: i32 = 20;
    let harga: f64 = 14999.50;
    let tersedia: bool = true;
    let kode: char = 'A';
    let nama_produk: &str = "Keyboard Mekanik";

    println!("ID: {}", id);
    println!("Produk: {}", nama_produk);
    println!("Kode kategori: {}", kode);
    println!("Harga: {}", harga);
    println!("Tersedia: {}", tersedia);

    stok = stok - 1;
    println!("Sisa stok: {}", stok);
}

Program ini menunjukkan beberapa praktik dasar yang baik:

  • gunakan immutable untuk data yang tidak berubah seperti id dan harga,
  • gunakan mut hanya untuk data yang memang berubah seperti stok,
  • beri anotasi tipe ketika itu membantu memperjelas domain data.

Trade-off, Batasan, dan Tips Debugging

Trade-off Pendekatan Immutable Default

Meskipun sangat berguna, pendekatan immutable default punya konsekuensi kecil: Anda mungkin merasa perlu menulis mut lebih sering saat membangun logika yang sangat stateful. Namun, trade-off ini justru memaksa Anda berpikir lebih hati-hati tentang aliran data.

Dalam praktiknya, hasilnya sering positif: fungsi menjadi lebih mudah dipahami, efek samping lebih terkendali, dan refactor menjadi lebih aman.

Kesalahan yang Sering Muncul

  • Lupa menambahkan mut saat ingin mengubah nilai.
  • Salah memilih tipe numerik, misalnya memakai integer padahal membutuhkan pecahan.
  • Tertukar antara char dan string.
  • Terlalu banyak anotasi tipe pada kode yang sebenarnya sudah jelas, sehingga kode menjadi lebih ramai tanpa manfaat besar.

Tips Membaca Error Compiler

Rust terkenal memiliki error message yang informatif. Jika Anda mendapat pesan seperti “cannot assign twice to immutable variable”, fokuslah pada deklarasi variabelnya. Jika error berkaitan dengan tipe, lihat apakah nilai yang diberikan sesuai dengan anotasi atau konteks penggunaannya.

Untuk pemula, kebiasaan terbaik adalah mencoba kode kecil, mengompilasinya, lalu membaca error sampai benar-benar paham penyebabnya. Ini jauh lebih efektif daripada sekadar menghafal sintaks.

Penutup

Memahami variabel, mutabilitas, dan tipe data dasar adalah langkah awal yang sangat penting dalam Rust. Konsep immutable default mungkin terasa tidak biasa pada awalnya, tetapi justru menjadi salah satu fondasi yang membuat Rust kuat dalam menjaga keamanan dan kejelasan program.

Ringkasnya, gunakan let untuk variabel yang tidak berubah, tambahkan mut hanya saat diperlukan, dan kenali tipe data dasar seperti integer, float, boolean, char, serta string sederhana. Dengan fondasi ini, Anda akan lebih siap memahami topik Rust berikutnya seperti kontrol alur, fungsi, ownership, dan struktur data.

Jika Anda baru mulai, fokuslah pada kebiasaan sederhana: tulis tipe saat membantu, jaga variabel tetap immutable bila memungkinkan, dan percayakan compiler Rust untuk membantu menangkap kesalahan lebih awal.