Bagi kita yang datang dari PHP atau Python, alur kerja Rust terasa berbeda: program tidak bisa langsung dijalankan dari berkas sumber, melainkan harus dikompilasi lebih dahulu. Perbedaan tersebut bukan sekadar aturan administratif — perbedaan tersebut mengubah cara kita membaca pesan galat, cara mendistribusikan program, dan cara mengukur performa. Pada Chapter ini kita akan membedah model kompilasi Rust: peran rustc, peran cargo, isi direktori target, serta perbedaan hasil build debug dan release.
Terkompilasi, Bukan Diinterpretasi #
PHP dan Python menginterpretasi kode saat program berjalan: interpreter membaca berkas sumber baris demi baris, menerjemahkan, lalu mengeksekusinya. Konsekuensinya, kesalahan di baris ke-50 baru terlihat ketika eksekusi sampai ke baris tersebut. Java berada di tengah: kode dikompilasi menjadi bytecode, lalu dijalankan di atas virtual machine.
Rust memakai model ahead-of-time compilation: seluruh kode sumber diterjemahkan menjadi machine code asli milik prosesor target sebelum program pernah dijalankan. Hasil akhirnya adalah satu berkas binary mandiri — tidak ada interpreter, tidak ada virtual machine, dan tidak ada runtime tambahan yang perlu dipasang di komputer pengguna. Kompilator juga memeriksa seluruh berkas sekaligus, sehingga kesalahan tipe di baris mana pun ketahuan sebelum program sempat berjalan.
Alur lengkapnya dapat kita petakan sebagai berikut:
Kode sumber masuk ke kompilator, hasilnya berupa binary di salah satu profil build, dan binary itulah yang dieksekusi — bukan berkas sumbernya.
rustc: Kompilator Inti #
rustc adalah kompilator Rust sesungguhnya. Seluruh proses penerjemahan bahasa dilakukan oleh program ini: membaca berkas sumber, memeriksa aturan tipe dan ownership, lalu menghasilkan machine code. Untuk berkas tunggal, rustc dapat dipakai langsung tanpa perantara. Buat berkas halo.rs berisi:
fn main() {
println!("Halo dari binary Rust!");
}Kompilasi dan jalankan:
rustc --edition 2021 halo.rs
./haloOutput yang Dihasilkan:
Halo dari binary Rust!Perintah pertama menghasilkan berkas binary bernama halo di folder yang sama dengan sumbernya, dan perintah kedua menjalankan binary tersebut. Binary hasil rustc berdiri sepenuhnya sendiri: pustaka standar Rust di-link secara statis ke dalamnya, sehingga berkas ini dapat disalin ke komputer Linux lain dengan arsitektur sama dan tetap berjalan tanpa instalasi Rust. Ukurannya sekitar 4,4 MB meskipun programnya hanya tiga baris — wajar, karena seluruh pustaka standar dan informasi debug ikut terbungkus di dalamnya.
Kendati rustc mampu bekerja sendirian, pemakaian langsung seperti ini hanya cocok untuk program kecil. Begitu jumlah berkas bertambah dan dependency eksternal muncul, kita butuh orkestrator: di situlah cargo masuk.
Cargo sebagai Pembungkus rustc #
Cargo tidak menggantikan rustc; cargo memanggil rustc di belakang layar dengan urutan dan opsi yang benar. Ketika kita menjalankan cargo build, cargo membaca Cargo.toml, menyiapkan opsi kompilasi (termasuk edition), mengompilasi dependency lebih dahulu, lalu memanggil rustc untuk setiap crate di proyek. Perhatikan kembali jejak prosesnya pada proyek demo_build:
cd demo_build
cargo buildOutput yang Dihasilkan:
Compiling demo_build v0.1.0 (/tmp/demo_build)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09sBaris Compiling menunjukkan cargo sedang memanggil rustc untuk paket demo_build, sedangkan baris Finished menyatakan proses selesai pada profil dev. Kata kunci dev inilah pintu menuju topik berikutnya: Rust menghasilkan dua ragam binary yang berbeda karakter.
Direktori target #
Seluruh hasil kompilasi ditempatkan cargo di satu direktori khusus bernama target. Mari kita intip isinya setelah build pertama:
ls target
ls target/debugOutput yang Dihasilkan:
CACHEDIR.TAG
debug
build
demo_build
demo_build.d
deps
examples
incrementalDaftar pertama adalah isi target/, sedangkan daftar kedua adalah isi target/debug/. Masing-masing punya peran:
target/debug/demo_build: binary hasil kompilasi yang siap dijalankan — berkas inilah yang dijalankancargo runpada Chapter sebelumnya.target/debug/demo_build.d: berkas kecil berisi catatan dependency berkas; cargo memakainya untuk menentukan berkas mana yang perlu dikompilasi ulang saat kode berubah.- Folder
deps/: menampung hasil kompilasi crate-crate pendukung, termasuk salinan intermediate milik paket kita sendiri. - Folder
build/: berkas kerja milik build script dependency — bagian ini baru terisi ketika proyek memakai crate yang membangun kode native. - Folder
incremental/: cache kompilasi inkremental yang membuat build ulang setelah perubahan kecil menjadi cepat. CACHEDIR.TAG: penanda bahwa direktori ini adalah cache yang boleh dihapus kapan saja.
Profil debug dan release #
Cargo mengenali dua profil utama dengan tujuan yang berlawanan. Profil debug (bernama dev secara internal) adalah default sehari-hari: kompilasi cepat, binary berisi informasi debugging, dan tidak ada optimasi. Profil release mengutamakan kecepatan jalan program: kompilator menyalakan optimasi hingga level tertinggi sehingga proses build lebih lama, tetapi binary hasilnya jauh lebih ramping dan cepat.
Mari kita bangun kedua ragam pada proyek yang sama:
cargo build --releaseOutput yang Dihasilkan:
Compiling demo_build v0.1.0 (/tmp/demo_build)
Finished `release` profile [optimized] target(s) in 0.05sBinary release muncul di target/release/ (terpisah dari target/debug/). Membandingkan ukuran keduanya memberi gambaran nyata:
ls -lh target/debug/demo_build
ls -lh target/release/demo_buildOutput yang Dihasilkan:
-rwxrwxr-x 2 z z 4.4M Oct 13 20:00 target/debug/demo_build
-rwxrwxr-x 2 z z 435K Oct 13 20:00 target/release/demo_buildKolom kelima pada keluaran ls -lh adalah ukuran berkas: binary debug berukuran 4.4M, sedangkan binary release hanya 435K. Kolom izin, pemilik, dan tanggal akan berbeda di komputer kita masing-masing. Optimasi menyusutkan kode, memangkas informasi debug, dan merapikan instruksi machine code — dari 4,4 MB menjadi sekitar sepersepuluh ukurannya. Jalankan binary release untuk membuktikan programnya tetap sama:
./target/release/demo_buildOutput yang Dihasilkan:
Hello, world!Ringkasan #
Rust menerjemahkan kode menjadi binary native sebelum program dijalankan: rustc menjalankan penerjemahan inti, cargo mengorkestrasi pemanggilannya, dan seluruh hasil kerja dikumpulkan di direktori target. Profil debug menyajikan siklus kembangkan yang cepat, sementara profil release menghasilkan binary ramping untuk distribusi. Dengan model kompilasi yang jelas, kita kini siap menghadapi sisi kompilator yang paling sering dijumpai pemula: pesan error — dan cara membacanya secara sistematis.