Sejak program pertama kita, kata main selalu hadir tanpa pernah dibedah. Kita tahu program “mulai dari situ”, tetapi belum pernah bertanya bagaimana sistem operasi menemukannya, bagaimana program memberi tahu bahwa eksekusi berhasil atau gagal, dan bagaimana program menerima data dari pengguna saat dijalankan. Pada Chapter ini kita akan memperlakukan main sebagai fungsi yang utuh: titik masuk (entry point) yang dipanggil oleh runtime Rust, penghasil exit code yang dibaca sistem operasi, dan pemilik argument baris perintah yang diketik pengguna.

main sebagai Titik Masuk Program #

Runtime Rust membutuhkan satu titik yang pasti untuk memulai eksekusi, dan titik tersebut adalah fungsi main. Saat kita menjalankan berkas hasil kompilasi, runtime menyiapkan lingkungan eksekusi (tumpukan memori, penanganan galat dasar), lalu memanggil fungsi main milik program kita. Ketika main selesai, runtime membersihkan sisanya dan mengembalikan kontrol ke sistem operasi.

Dua konsekuensi penting mengikuti desain ini. Pertama, setiap program Rust yang bisa dieksekusi wajib punya tepat satu fungsi main; tanpa main, kompilasi gagal dengan galat error[E0601]: `main` function not found. Kedua, fungsi selain main tidak pernah berjalan otomatis — sekuat apa pun fungsinya didefinisikan, fungsi hanya hidup kalau dipanggil, dan seluruh rantai pemanggilan pasti berujung pada main.

Membaca Argumen Baris Perintah #

Program CLI (command line interface) menerima masukan langsung dari perintah yang diketik pengguna, misalnya ./sapa Budi mengirim kata Budi ke program sapa. Rust menyediakan akses ke daftar tersebut melalui std::env::args(). Mari kita buat program pemeriksa argument:

rust
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    println!("Jumlah argumen: {}", args.len());
    println!("Nama program: {}", args[0]);

    if args.len() > 1 {
        println!("Argumen pertama: {}", args[1]);
    }
}

Kompilasi dan jalankan dengan satu argument tambahan:

bash
rustc --edition 2021 daftar-argumen.rs -o daftar-argumen
./daftar-argumen Budi

Output yang Dihasilkan:

text
Jumlah argumen: 2
Nama program: ./daftar-argumen
Argumen pertama: Budi

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 1 (use std::env;): Statement use membawa modul env dari pustaka standar ke ruang nama berkas, sehingga kita cukup menulis env::args() tanpa jalur lengkap std::env::args().
  2. Baris 4 (let args: Vec<String> = env::args().collect();): env::args() menghasilkan argument satu per satu; .collect() mengumpulkannya menjadi Vec<String>, yaitu kumpulan (semacam daftar) yang setiap elemennya bertipe String. Anotasi Vec<String> memberi tahu kompiler bentuk kumpulan yang kita inginkan.
  3. Baris 6 (println!("Jumlah argumen: {}", args.len());): args.len() menghitung elemen koleksi. Hasilnya 2 karena nama program ikut dihitung sebagai argument pertama.
  4. Baris 7 (println!("Nama program: {}", args[0]);): Elemen ke-0 adalah teks yang dipakai untuk menjalankan program, dalam kasus ini ./daftar-argumen. Indeks selalu dimulai dari nol.
  5. Baris 9–11 (if args.len() > 1 { ... }): Pemeriksaan ini menjaga agar program tidak membaca args[1] saat pengguna tidak mengirim argument apa pun, karena posisi tersebut belum tentu ada. Argument pengguna yang pertama selalu berada di args[1].

Semua argument dari baris perintah datang sebagai String — termasuk angka. Kalau program butuh angka untuk dihitung, teks tersebut harus dikonversi terlebih dahulu, topik yang akan kita dalami saat membahas penanganan error.

Exit Code dan Sinyal Berhasil atau Gagal #

Setiap program yang berakhir meninggalkan satu angka untuk sistem operasi, bernama exit code. Konvensinya: 0 berarti sukses, angka selain 0 berarti gagal. Angka ini bukan hiasan — skrip bash, CI/CD, dan alat otomasi lain membacanya lewat variabel khusus $? untuk memutuskan langkah berikutnya, misalnya menghentikan deployment saat program gagal.

Saat main selesai secara wajar tanpa masalah, Rust otomatis melaporkan exit code 0. Untuk mengatur angkanya sendiri, kita memakai std::process::exit:

rust
use std::process;

fn main() {
    let args: Vec<String> = std::env::args().collect();

    if args.len() < 2 {
        println!("Cara pakai: {} <nama>", args[0]);
        process::exit(1);
    }

    println!("Halo, {}! Selamat datang kembali.", args[1]);
    process::exit(0);
}

Uji kedua skenarionya beserta exit code-nya:

bash
rustc --edition 2021 sapa.rs -o sapa
./sapa
echo $?
./sapa Budi
echo $?

Output yang Dihasilkan:

text
Cara pakai: ./sapa <nama>
1
Halo, Budi! Selamat datang kembali.
0

Mari kita telaah alur kedua eksekusi di atas:

  1. Baris 6 (if args.len() < 2): Program memeriksa jumlah argument lebih dulu. Pola “periksa dulu, gagal cepat” seperti ini adalah bentuk klasik pemanggilan process::exit — kesalahan penggunaan dideteksi sebelum pekerjaan utama dimulai.
  2. Baris 8 (process::exit(1);): Saat argument kurang, program mencetak petunjuk pemakaian lalu menghentikan seluruh proses seketika dengan exit code 1. Baris-baris setelah pemanggilan ini tidak pernah dieksekusi pada skenario pertama.
  3. Baris 11–12 (println!(...) dan process::exit(0);): Saat argument lengkap, program menyapa pengguna lalu berakhir dengan exit code 0.
  4. Perintah echo $?: Bash mencetak exit code dari program yang baru saja selesai. Skenario tanpa argument menghasilkan 1, skenario dengan Budi menghasilkan 0.

Pemanggilan process::exit di akhir dengan kode 0 pada contoh di atas sengaja ditampilkan untuk kejelasan; pada praktiknya, baris tersebut bisa dihapus karena Rust sudah melaporkan 0 saat main selesai dengan wajar. Yang tidak boleh dihapus adalah pemanggilan dengan kode galat di jalur kegagalan.

Ringkasan #

Fungsi main adalah titik masuk yang dipanggil runtime Rust; tanpa main, program gagal dikompilasi. Argument baris perintah diakses melalui std::env::args() sebagai koleksi String dengan nama program di posisi pertama. Exit code 0 menandakan sukses dan dilaporkan otomatis saat main selesai, sedangkan std::process::exit(kode) menghentikan program seketika dengan kode yang kita pilih untuk jalur kegagalan. Program kita kini bisa menerima masukan dan melaporkan status ke lingkungannya — dua kemampuan yang membuat Rust cocok untuk perangkat CLI. Selanjutnya kita membedah ruang hidup variabel di dalam fungsi.