Apa itu Rust ?

Sejarah Rust, keamanan memori tanpa garbage collection, serta wilayah keunggulannya.

Daftar isi

Rust adalah bahasa pemrograman sistem untuk perangkat lunak yang berjalan dekat dengan perangkat keras, misalnya sistem operasi, perangkat tertanam, dan layanan jaringan. Topik ini membahas asal-usul Rust di Mozilla, mekanisme keamanan memori tanpa garbage collection (pengumpulan sampah), serta wilayah penerapan yang menjadi kekuatan utama Rust. Setelah topik ini selesai, dasar untuk memahami model bahasa dan rantai alat Rust sudah terbentuk.

Sejarah singkat Rust#

Rust dirintis oleh Graydon Hoare sebagai proyek pribadi sekitar tahun 2006, kemudian dibesarkan di Mozilla yang mengumumkan dukungan atas proyek ini pada 2010. Tujuan awalnya adalah bahasa dengan keamanan memori seperti bahasa yang memakai garbage collector, tetapi dengan kinerja dan kendali memori setara C++. Mozilla kemudian memakai Rust untuk membangun peramban eksperimental Servo yang mulai dikembangkan pada 2012. Kebutuhan nyata Servo mendorong kematangan pustaka standar maupun compiler Rust.

Selama masa sebelum versi stabil, sintaks dan pustaka Rust masih sering berubah sehingga kode lama perlu disesuaikan berkali-kali. Versi 1.0 yang dirilis pada 15 Mei 2015 menandai tonggak stabilisasi. Sejak versi tersebut, kompatibilitas mundur dijaga sebagai janji resmi proyek, dan Rust menerbitkan versi baru setiap enam pekan melalui kanal stable (jalur rilis utama). Bahasa tetap berkembang melalui proses pengambilan keputusan yang terbuka, salah satunya lewat dokumen RFC, yaitu usulan perubahan desain yang dapat dibaca siapa saja.

Pada 2021, dukungan organisasi berpindah ke Rust Foundation, yayasan nirlaba yang didirikan AWS, Google, Huawei, Microsoft, dan Mozilla. Yayasan ini membiayai infrastruktur seperti server build dan mengelola merek dagang Rust, sedangkan pengembangan bahasa tetap dikerjakan tim-tim kontributor sukarela. Rust kini merupakan proyek open source besar dengan ribuan kontributor pada setiap rilis.

Keamanan memori tanpa garbage collection#

Kesalahan memori seperti use-after-free (pemakaian memori yang sudah dibebaskan), double free (pembebasan ganda pada alamat yang sama), dan akses di luar batas buffer merupakan sumber kerentanan keamanan yang paling lama dikenal pada program C dan C++. Sebelum Rust, terdapat dua pendekatan utama untuk menekan kelas kesalahan tersebut. Pendekatan pertama membiarkan programer mengelola memori secara manual, yang efisien tetapi mudah salah dan kesalahannya baru terlihat saat program berjalan. Pendekatan kedua memakai garbage collector (pengumpul sampah), yaitu runtime yang memindai memori dan membebaskan bagian yang tidak terpakai, seperti pada Java, Go, dan Python.

Rust menempuh jalan ketiga melalui ownership (kepemilikan). Setiap nilai memiliki tepat satu pemilik, dan ketika pemilik keluar dari lingkupnya, memori untuk nilai tersebut dibebaskan seketika. Aturan tentang siapa yang boleh membaca atau mengubah sebuah nilai diperiksa oleh borrow checker pada waktu kompilasi, bukan pada waktu program berjalan. Dengan model ini, banyak kesalahan memori tertangkap sebelum program dieksekusi, tanpa runtime pengumpul sampah dan tanpa jeda pemindaian.

Tiga pendekatan tersebut dapat diringkas sebagai berikut.

AspekAlokasi manual (C, C++)Garbage collector (Java, Go)Ownership (Rust)
Penentu pembebasan memoriprogramerruntime yang memindai memoripemilik nilai saat lingkup berakhir
Pemeriksaan kesalahan memorisangat sedikit saat kompilasisebagian saat runtimesaat kompilasi oleh borrow checker
Jeda eksekusi yang tidak terdugatidak adadapat muncul saat pemindaiantidak ada

Perilaku pembebasan memori pada Rust dapat diamati langsung lewat program berikut. Program memakai blok impl Drop agar momen pembebasan memori tercetak ke layar, lalu membuat dua sensor pada lingkup yang berbeda.

rust siklus-hidup.rs
struct SensorSuhu {
    kota: &'static str,
    suhu: i32,
}

// Drop berjalan otomatis saat pemilik nilai keluar dari lingkupnya
impl Drop for SensorSuhu {
    fn drop(&mut self) {
        println!("memori sensor {} dibebaskan", self.kota);
    }
}

fn main() {
    let sensor_a = SensorSuhu { kota: "Bandung", suhu: 27 };
    {
        let sensor_b = SensorSuhu { kota: "Semarang", suhu: 30 };
        println!("suhu di {}: {} derajat", sensor_b.kota, sensor_b.suhu);
    }
    println!("suhu di {}: {} derajat", sensor_a.kota, sensor_a.suhu);
    println!("program selesai");
}
text Keluaran
suhu di Semarang: 30 derajat
memori sensor Semarang dibebaskan
suhu di Bandung: 27 derajat
program selesai
memori sensor Bandung dibebaskan

Keluaran di atas memperlihatkan dua fakta. Pertama, memori sensor Semarang dibebaskan sebelum baris suhu Bandung tercetak karena lingkup dalam {} sudah berakhir sebelum baris tersebut dijalankan. Kedua, sensor Bandung baru dibebaskan setelah baris terakhir main selesai karena pemiliknya, variabel sensor_a, berada di lingkup fungsi. Fungsi drop berjalan otomatis dan tidak pernah dipanggil manual, sehingga saat pembebasan memori dapat diprediksi hanya dengan membaca struktur program.

Sisi lain dari model ini adalah penolakan kode oleh rustc pada waktu kompilasi. Program berikut meminjam daftar melalui referensi pertama, lalu mengubah daftar selagi pinjaman itu masih dipakai. Simpan sebagai pinjam.rs bila ingin mencoba mengompilasinya dan melihat penolakannya secara langsung.

rust Program pinjam.rs yang ditolak rustc
// tidak-dijalankan
fn main() {
    let mut daftar = vec![10, 20, 30];
    let pertama = &daftar[0];
    daftar.push(40); // ditolak rustc: daftar masih dipinjam oleh pertama
    println!("elemen pertama: {pertama}");
}

rustc menolak program tersebut dengan pesan error berikut.

text Pesan error rustc (dipangkas)
error[E0502]: cannot borrow `daftar` as mutable because it is also borrowed as immutable
 --> pinjam.rs:5:5
  |
4 |     let pertama = &daftar[0];
  |                    ------ immutable borrow occurs here
5 |     daftar.push(40); // ditolak rustc: daftar masih dipinjam oleh pertama
  |     ^^^^^^^^^^^^^^^ mutable borrow occurs here

Pesan itu memuat kode diagnosis E0502, potongan kode yang menjadi lokasi masalah, dan penjelasan singkat dalam bahasa Inggris. Tanpa pemeriksaan semacam ini, pola pinjam-lalu-ubah dapat membuat referensi menunjuk ke memori yang sudah tidak berlaku, yaitu bentuk use-after-free. Pada Rust, masalah seperti ini tercetak sebelum program sempat berjalan, bukan setelah menimbulkan kegagalan di production.

Wilayah keunggulan Rust#

Dua sifat pada bagian sebelumnya, yaitu pemeriksaan keamanan memori saat kompilasi dan ketiadaan runtime pengumpul sampah, menentukan wilayah tempat Rust memberikan manfaat paling besar. Empat wilayah berikut merupakan contoh penerapan yang paling lazim.

  • Sistem tertanam. Perangkat kecil dengan memori terbatas dan kebutuhan waktu nyata yang ketat tidak selalu dapat menyediakan ruang untuk runtime tambahan. Rust menghasilkan berkas biner yang ramping serta mendukung mode no_std untuk perangkat tanpa sistem operasi.
  • Perkakas baris perintah (CLI). Program CLI hasil kompilasi Rust dibagikan sebagai satu berkas biner sehingga pengguna tidak perlu memasang runtime bahasa apa pun. Pemeriksaan tipe saat kompilasi juga menahan regresi, yakni rusaknya fitur yang sebelumnya berfungsi, ketika kode perkakas makin panjang.
  • WebAssembly. rustc dapat membangun kode Rust menjadi modul WebAssembly yang berjalan di dalam peramban. Ukuran hasil yang kecil penting karena modul dikirim melalui jaringan sebelum dipakai.
  • Jaringan dan layanan. Layanan yang berjalan berbulan-bulan memperoleh manfaat ganda: keamanan memori menekan kelas kerentanan tertentu, dan kompilasi penuh sebelum eksekusi menghasilkan latensi yang stabil.

Adopsi pada perangkat lunak besar juga sudah terjadi. Kernel Linux menerima dukungan Rust untuk driver perangkat sejak versi 6.1, sementara sebagian komponen sistem Android dan Windows ditulis ulang dalam Rust. Capaian tersebut menunjukkan bahwa model keamanan Rust dapat dipakai sampai ke tingkat sistem operasi, bukan hanya pada aplikasi pengguna.

Kesalahan umum#

  • “Rust membuat program bebas dari segala bug.” Keamanan memori Rust menyasar kelas kesalahan memori seperti use-after-free dan akses di luar batas buffer. Kesalahan logika seperti rumus yang salah tetap dapat terjadi. Perbaikannya adalah tetap menulis pengujian dan tinjauan kode seperti pada bahasa lain.
  • “Tanpa garbage collector berarti mengatur malloc dan free sendiri.” Tidak ada pemanggilan manual seperti itu pada Rust. Penentuan pembebasan memori dikerjakan compiler melalui aturan ownership. Perbaikannya adalah membaca error compiler sebagai umpan balik tentang struktur kepemilikan data, bukan sebagai tanda untuk mengelola memori secara manual.
  • “Rust hanya cocok untuk kernel dan perangkat tertanam.” Rust memang berakar pada pemrograman sistem, tetapi ekosistemnya juga menopang perkakas CLI, layanan web, dan komponen peramban. Penyebab kekeliruan adalah penekanan dokumentasi pada kinerja tingkat sistem. Perbaikannya adalah menilai Rust berdasarkan kebutuhan proyek, misalnya kebutuhan keamanan memori atau berkas biner mandiri.

Ringkasan#

  • Rust dirintis oleh Graydon Hoare, dibesarkan di Mozilla sejak 2010, mencapai versi 1.0 pada Mei 2015, dan kini ditopang oleh Rust Foundation.
  • Keamanan memori dicapai melalui ownership dan borrow checker pada waktu kompilasi, tanpa runtime garbage collector.
  • Pembebasan memori terjadi tepat saat pemilik nilai keluar dari lingkupnya, sehingga waktunya dapat diprediksi.
  • Wilayah keunggulan utama Rust mencakup sistem tertanam, perkakas CLI, WebAssembly, serta jaringan dan layanan.

Referensi#