Episode ini membedah cara kerja Rust di balik layar: kompilasi lewat LLVM, borrow checker, dan model ownership. Kalian juga mengenal cargo sebagai build system, package manager, dan workspace manager, serta komponen utama seperti crate, module, trait, impl, dan generic.

Episode 1 menjelaskan mengapa Rust ada. Episode 2 membedah bagaimana Rust bekerja. Kalian akan melihat perjalanan kode dari file sumber sampai binary: kompilasi lewat LLVM, pemeriksaan ownership oleh borrow checker, dan orchestrasi seluruhnya oleh cargo.
Memahami arsitektur ini bukan sekadar pengetahuan teori. Ketika kompiler menolak kode kalian, pemahaman atas borrow checker dan model ownership akan membuat pesan error terasa seperti petunjuk, bukan hambatan. Cargo juga akan menjadi kendaraan utama untuk seluruh project di series ini.
Rust tidak mengompilasi langsung ke bahasa mesin. Pipeline-nya berlapis: parser mengubah source menjadi AST, kemudian menjadi HIR dan MIR (mid-level intermediate representation), lalu dilakukan berbagai pemeriksaan dan optimasi, dan terakhir dikonversi menjadi kode mesin oleh LLVM.
LLVM adalah compiler infrastructure yang sudah matang, dipakai juga oleh C, C++, dan Swift. Rust memanfaatkannya untuk menghasilkan kode yang efisien di banyak platform. Karena itu target yang didukung sangat luas: dari embedded ARM sampai x86-64 server dan WebAssembly.
cargo build
cargo build --releasecargo build menghasilkan binary debug yang cepat dikompilasi, sementara cargo build --release mengaktifkan optimasi penuh. cargo build --release menghasilkan binary di target/release yang dipakai untuk production. Optimasi memakan waktu lebih lama tetapi performanya jauh lebih baik.
Aturan inti Rust: setiap nilai memiliki tepat satu pemilik, dan nilai dibebaskan saat pemilik keluar dari scope. Tidak ada garbage collector yang berjalan di background — pembebasan memori terjadi deterministik tepat di akhir scope.
Untuk memakai nilai tanpa memindahkan kepemilikan, Rust memakai referensi: satu referensi mutable atau banyak referensi immutable, tidak pernah keduanya sekaligus untuk nilai yang sama. Borrow checker adalah bagian kompiler yang memverifikasi aturan ini.
cat > borrow.rs <<'EOF'
fn hitung_panjang(s: &String) -> usize {
s.len()
}
fn main() {
let pesan = String::from("belajar rust");
let panjang = hitung_panjang(&pesan);
println!("panjang: {}", panjang);
}
EOF
rustc borrow.rs -o borrow
./borrowhitung_panjang(&pesan) meminjam pesan secara immutable, sehingga pesan tetap valid setelah fungsi selesai. Coba ubah &String menjadi &mut String dan kalian akan melihat borrow checker menegakkan aturannya.
Setiap referensi punya lifetime: seberapa lama referensi itu valid. Sebagian besar waktu lifetime bisa disimpulkan otomatis oleh kompiler. Anotasi eksplisit seperti 'a baru diperlukan saat beberapa referensi saling terkait — detail ini akan dibahas menyeluruh di episode 6.
Cargo bukan sekadar compiler wrapper. Dia menangani:
Cargo.toml dan Cargo.lock.Cargo.toml adalah manifest yang mendeklarasikan metadata, dependency, features, dan edition. Cargo.lock mematok versi persis dari setiap dependency agar build reproducible.
cargo metadata
cargo treecargo metadata menampilkan informasi lengkap project dalam format JSON, sedangkan cargo tree menampilkan pohon dependency. Keduanya berguna untuk audit dan debugging — cargo tree akan sering kalian pakai di episode 7.
Rust menyusun kode dalam empat level:
Cargo.toml dan satu atau lebih crate.main.rs) atau library (dengan lib.rs).mod.cargo new --lib kalkulator
find kalkulator -type fcargo new --lib kalkulator membuat library crate dengan src/lib.rs. Crate library adalah fondasi untuk membangun kode yang bisa diuji dan dipakai ulang, sementara binary biasanya tipis dan hanya memanggil library.
Trait adalah kontrak perilaku: mendefinisikan method yang harus dimiliki sebuah tipe. Impl adalah implementasi trait tersebut untuk tipe tertentu. Gabungan keduanya memungkinkan kode generik yang bekerja untuk tipe apa pun yang memenuhi kontrak.
Generic memungkinkan fungsi menulis satu definisi yang bekerja untuk banyak tipe, dengan monomorphization: compiler men-generate kode spesifik per tipe saat kompilasi. Inilah contoh zero-cost abstraction — abstraksi yang tidak menambah biaya runtime.
cat > trait_generic.rs <<'EOF'
trait Deskripsi {
fn deskripsi(&self) -> String;
}
struct Server {
nama: String,
}
impl Deskripsi for Server {
fn deskripsi(&self) -> String {
format!("server {}", self.nama)
}
}
fn cetak<T: Deskripsi>(item: &T) {
println!("{}", item.deskripsi());
}
fn main() {
let s = Server { nama: String::from("api-utama") };
cetak(&s);
}
EOF
rustc trait_generic.rs -o trait_generic
./trait_genericfn cetak<T: Deskripsi>(item: &T) adalah fungsi generic yang menerima tipe apa pun yang mengimplementasi Deskripsi. Episode 5 akan membedah trait dan generic secara mendalam.
Inti yang harus dibawa pulang:
Di episode 3 selanjutnya kita akan membahas menulis program Rust pertama — struktur crate binary dan library, tipe data dasar, variable immutability dan shadowing, fungsi dan expression, serta control flow dan pattern matching. Kalian akan mulai menulis kode Rust yang sesungguhnya.