Cara Kerja Rust

rustc, kompilasi ahead-of-time, dan mental model zero-cost abstraction.

Daftar isi

Topik ini membahas jalan yang dilalui kode Rust dari berkas sumber sampai menjadi program yang berjalan: peran rustc sebagai compiler, model kompilasi ahead-of-time, dan mental model zero-cost abstraction. Setelah membaca topik ini, tahapan di balik perintah build dapat diikuti langkah demi langkah, bukan diperlakukan sebagai kotak hitam. Seluruh contoh pada topik ini dapat dijalankan dengan rustc saja, tanpa proyek Cargo.

rustc sebagai compiler utama#

rustc adalah compiler resmi Rust. Program ini membaca berkas kode sumber dan menerjemahkannya menjadi berkas biner berisi kode mesin untuk arsitektur serta sistem operasi target. Pada praktik sehari-hari rustc jarang dipanggil langsung karena Cargo, sistem build resmi Rust, sudah memanggilnya dengan konfigurasi yang tepat. Meskipun begitu, satu putaran kompilasi manual memperlihatkan dengan jelas pekerjaan yang sebenarnya terjadi di balik perintah Cargo.

Periksa versi compiler yang terpasang sebelum mulai. Baris export PATH pada blok berikut memastikan direktori ~/.cargo/bin terbaca oleh sesi shell yang sedang berjalan, karena terminal yang dibuka sebelum pemasangan rustup tidak melihat perubahan PATH yang dibuat skrip pemasangan.

bash
# pastikan rantai alat Rust berada di PATH sesi shell ini
export PATH="$HOME/.cargo/bin:$PATH"
rustc --version
text Keluaran
rustc 1.99.1 (58cf8f9c3 2026-10-13)

Baris keluaran memuat nomor versi, kode hash build, dan tanggal rilis. Isi baris tersebut mengikuti versi rustc yang terpasang sehingga dapat berbeda pada komputer lain.

Buat berkas halo.rs dengan isi berikut, lalu kompilasi dan jalankan lewat rustc.

rust halo.rs
fn main() {
    println!("Halo, dunia!");
    println!("Program ini menjadi berkas biner lewat rustc.");
}
text Keluaran
Halo, dunia!
Program ini menjadi berkas biner lewat rustc.
bash
# pastikan rantai alat Rust berada di PATH sesi shell ini
export PATH="$HOME/.cargo/bin:$PATH"
# kompilasi halo.rs menjadi berkas biner bernama halo
rustc --edition=2021 halo.rs
./halo
text Keluaran
Halo, dunia!
Program ini menjadi berkas biner lewat rustc.

Blok pertama memperlihatkan isi berkas halo.rs beserta keluaran programnya, sedangkan blok bash sesudahnya memperlihatkan perintah kompilasi beserta keluarannya di terminal. Kedua blok menampilkan keluaran yang sama karena satu-satunya perbedaan adalah cara menjalankan program.

rustc tidak mencetak apa pun ketika kompilasi berhasil. Perilaku ini mengikuti kebiasaan perkakas Unix yang tidak menampilkan pesan pada saat berhasil. Sebaliknya, ketika kompilasi gagal, rustc menampilkan pesan error lengkap dengan nomor baris dan kode diagnosis, lalu berhenti tanpa menghasilkan berkas biner.

Opsi --edition=2021 memilih edisi bahasa yang dipakai compiler membaca kode. Rust mengenal edition (edisi bahasa) 2015, 2018, 2021, dan 2024 sebagai mekanisme evolusi yang tetap menjaga kompatibilitas program lama. Pada proyek Cargo, edisi dicatat di Cargo.toml sehingga opsi ini tidak perlu diketik setiap kali membangun.

Kompilasi ahead-of-time#

Kompilasi ahead-of-time berarti seluruh penerjemahan kode sumber menjadi kode mesin selesai dikerjakan sebelum program dijalankan. Hasilnya adalah berkas biner yang dieksekusi langsung oleh sistem operasi. Lawannya adalah penerjemahan yang terjadi sambil program berjalan: interpreter membaca dan mengeksekusi kode baris demi baris, sedangkan mesin runtime dengan kompilasi just-in-time (JIT) menerjemahkan bytecode (kode antara) menjadi kode mesin ketika program sedang dieksekusi.

Pembagian model eksekusi dapat diringkas sebagai berikut.

Model eksekusiContoh bahasaSaat penerjemahanBerkas yang dibagikan
InterpretasiPython, Rubysambil program berjalankode sumber
Bytecode dengan JITJava, C#sebagian saat pemuatan, sebagian saat berjalanbytecode
Ahead-of-timeC, C++, Go, Rustseluruhnya sebelum program berjalanberkas biner

Pembagian tersebut menyederhanakan variasi antarimplementasi, misalnya CPython juga menerjemahkan kode sumber menjadi bytecode internal sebelum bytecode itu diinterpretasikan. Meskipun begitu, perbedaan intinya tetap berlaku: pada Rust, tidak ada tahap penerjemahan yang tertunda sampai program berjalan.

Konsekuensi pertama menyangkut kecepatan saat program dimulai serta distribusi berkas biner. Program hasil kompilasi ahead-of-time mulai berjalan tanpa tahap pemanasan karena kode mesin sudah siap, dan berkas binernya dapat dibagikan ke komputer lain tanpa memasang runtime bahasa, selama arsitektur serta pustaka sistemnya sesuai. Konsekuensi kedua adalah biaya di muka: waktu kompilasi lebih panjang, apalagi karena rustc sekaligus menjalankan pemeriksaan keamanan memori seperti borrow checker pada tahap ini.

Alur lengkapnya ringkas: satu berkas sumber masuk, satu berkas biner keluar, dan berkas biner itu dijalankan langsung oleh sistem operasi. Diagram berikut meringkas alur tersebut.

main.rskode sumberdibacarustccompilermenghasilkanhaloberkas binerberkas biner dijalankan langsung oleh sistem operasi target
Alur kompilasi rustc dari kode sumber menjadi berkas biner

Mental model zero-cost abstraction#

Zero-cost abstraction (abstraksi tanpa biaya) adalah janji bahwa pemakaian abstraksi tingkat tinggi tidak menambah biaya saat program berjalan dibanding kode tingkat rendah yang ditulis secara manual untuk kasus yang sama. Prinsip ini diwariskan dari tradisi C++ dan dipegang ketat oleh desain Rust. Isinya terangkum dalam dua bagian: abstraksi yang tidak dipakai tidak menimbulkan biaya, dan abstraksi yang dipakai tidak dapat ditulis lebih efisien secara manual.

Mekanisme utamanya bernama monomorphization. Ketika sebuah fungsi generik dipanggil dengan tipe konkret, compiler membuat salinan kode khusus untuk kombinasi tipe tersebut. Hasil akhirnya adalah pemanggilan langsung antarfungsi khusus. Pemanggilan tersebut masih dapat dioptimasi lebih lanjut, tanpa pencarian tipe saat program berjalan dan tanpa objek perantara. Lapisan generik lenyap pada saat kompilasi, dan itulah makna “tanpa biaya”.

Program berikut mendefinisikan satu fungsi generik terbesar, lalu memakainya untuk tiga tipe yang berbeda.

rust terbesar.rs
// T: PartialOrd berarti tipe yang nilainya dapat dibandingkan
fn terbesar<T: PartialOrd>(a: T, b: T) -> T {
    if a > b {
        a
    } else {
        b
    }
}

fn main() {
    let angka = terbesar(24, 10);
    let suhu = terbesar(27.5, 30.1);
    let huruf = terbesar('a', 'm');
    println!("angka terbesar: {angka}");
    println!("suhu terbesar: {suhu}");
    println!("huruf terbesar: {huruf}");
}
text Keluaran
angka terbesar: 24
suhu terbesar: 30.1
huruf terbesar: m

Satu definisi terbesar dipakai untuk bilangan bulat, bilangan pecahan, dan karakter. Pada saat kompilasi, rustc membuat tiga salinan fungsi tersebut, masing-masing khusus untuk i32, f64, dan char, sehingga ketiga pemanggilan di main menjadi pemanggilan fungsi biasa. Batasan T: PartialOrd menyatakan syarat tipe yang boleh masuk, yaitu tipe yang mendukung pembandingan urutan. Sistem trait dan generics dibahas tuntas pada modul Trait dan Generics di jalur ini.

Kesalahan umum#

  • Menunggu keluaran ketika kompilasi berhasil. rustc mengikuti konvensi Unix bahwa keberhasilan tidak mencetak apa pun, sehingga layar terminal tetap kosong. Penyebab kekeliruan adalah terbiasa dengan perkakas yang selalu mencetak laporan proses. Perbaikannya adalah memeriksa kode keluar dengan echo $? atau memeriksa keberadaan berkas biner hasil kompilasi dengan ls.
  • Menganggap semua abstraksi Rust gratis. Janji zero-cost berlaku pada pola yang diketahui compiler, seperti fungsi generik dan iterator. Pemanggilan melalui dyn Trait tetap melewati satu lompatan tak langsung, dan alokasi memori tetap menjadi alokasi memori. Perbaikannya adalah memilih bentuk abstraksi sesuai kebutuhan dan mengukur kinerja ketika kinerja benar-benar menjadi masalah.
  • Melupakan opsi edisi pada kompilasi manual. Kompilasi dengan rustc tanpa --edition memakai edisi 2015 sebagai bawaan, sehingga sejumlah fitur bahasa baru tidak dapat dipakai. Perbaikannya adalah selalu menyertakan --edition=2021 pada kompilasi manual, atau memakai Cargo yang membaca edisi dari Cargo.toml.

Ringkasan#

  • rustc menerjemahkan berkas sumber Rust menjadi berkas biner kode mesin, dan Cargo memanggilnya secara otomatis dengan konfigurasi proyek.
  • Kompilasi terjadi seluruhnya sebelum program berjalan, sehingga berkas biner dapat didistribusikan tanpa runtime bahasa dan mulai berjalan tanpa pemanasan.
  • Opsi --edition pada rustc dan kolom edition pada Cargo.toml memilih edisi bahasa.
  • Fungsi generik dimonomorfisasi menjadi salinan khusus per tipe sehingga abstraksi tidak menambah biaya saat program berjalan.

Referensi#