Seluruh section ini berakhir di satu tempat: main. Selama ini kita selalu menutup rantai propagasi dengan match di dalam main — padahal main hanyalah fungsi biasa, dan fungsi biasa boleh mengembalikan Result. Program baris perintah memang sangat cocok untuk pola tersebut: argumen bisa hilang, teks argumen bisa gagal diparse, dan satu-satunya reaksi yang wajar adalah mencetak laporan lalu keluar dengan exit code yang menyatakan kegagalan. Rust mendukung bentuk fn main() -> Result<(), Box<dyn Error>> secara khusus — Err yang keluar dari main otomatis dicetak sebagai pesan Error: ... dan proses keluar dengan exit code 1. Pada chapter ini kita akan membangun program CLI kecil lengkap dengan argumen, membedah notasi Box<dyn Error> secukupnya, dan membandingkan jalur keluar ini dengan panic.

Mengubah Tanda Tangan main #

Program kita bernama pendaftaran: pengguna menjalankannya dengan satu argumen berupa usia, dan program memverifikasi bahwa teks tersebut adalah angka. Ketiga kemungkinan — argumen sah, argumen rusak, argumen hilang — semuanya ditangani tanpa satu pun match:

rust
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let masukan = std::env::args()
        .nth(1)
        .ok_or("Sertakan usia sebagai argumen, misalnya: pendaftaran 34")?;

    let usia: u32 = masukan.parse()?;

    println!("Usia {} tahun akan tercatat di basis data", usia);
    Ok(())
}

Simpan sebagai main.rs, lalu kompilasi dan uji ketiga keadaannya:

bash
rustc --edition 2021 main.rs -o pendaftaran
./pendaftaran 34
echo "Exit code: $?"
./pendaftaran abc
echo "Exit code: $?"
./pendaftaran
echo "Exit code: $?"

Output yang Dihasilkan:

text
Usia 34 tahun akan tercatat di basis data
Exit code: 0
Error: ParseIntError { kind: InvalidDigit }
Exit code: 1
Error: "Sertakan usia sebagai argumen, misalnya: pendaftaran 34"
Exit code: 1

Mari kita bedah kode dan hasilnya:

  1. Baris 1 (use std::error::Error;): import trait Error dari pustaka standar agar penulisan Box<dyn Error> di tanda tangan cukup pendek.
  2. Baris 3 (fn main() -> Result<(), Box<dyn Error>>): tanda tangan baru main. Tipe suksesnya adalah () — unit type, nilai “tidak ada apa-apa” — karena program yang berhasil tidak perlu mengembalikan data; cukup menyatakan sukses lewat Ok(()).
  3. Baris 4-6 (std::env::args().nth(1).ok_or(...)?): std::env::args() menyediakan argumen baris perintah sebagai rangkaian teks; method .nth(1) mengambil elemen kedua — elemen pertama selalu nama program sendiri — dan menghasilkan Option<String> karena argumen tersebut bisa tidak ada. Method ok_or(pesan) mengubah Option menjadi Result: Some menjadi Ok, None menjadi Err(pesan). Operator ? kemudian mempropagasi Err keluar dari main saat argumen hilang.
  4. Baris 8 (let usia: u32 = masukan.parse()?;): pola lama dari chapter Operator Tanya; teks "abc" gagal diparse dan error-nya langsung keluar dari main.
  5. Baris 11 (Ok(())): nilai kembalian jalur sukses — Ok membawa unit (). Baris ini wajib ada karena main kini berjanji mengembalikan Result.
  6. Pesan Error: ... pada output: kedua jalur kegagalan dicetak otomatis oleh runtime Rust — kita tidak menulis satu pun println! untuk error. Isi Err ditampilkan dengan format {:?}: error parsing tampil sebagai struct mentah, pesan ok_or tampil sebagai string berkutip.
  7. Exit code: jalur sukses menghasilkan 0, kedua jalur Err menghasilkan 1 — tanpa kita menulis kode exit code apa pun.

Kontras dengan pendekatan lama sangat besar. Sebelumnya main harus membuka match untuk setiap operasi; kini main hanyalah daftar langkah yang masing-masing ditandai ?, dan seluruh pelaporan kegagalan berpusat di satu mekanisme.

Apa Itu Box #

Notasi Box<dyn Error> memuat dua bagian yang masing-masing punya tugas:

  • dyn Error — trait object untuk trait Error: satu nama tipe yang mewakili “semua tipe error yang mengimplementasikan trait Error”. ParseIntError, error I/O dari berkas, hingga tipe error buatan kita sendiri — semuanya sah menempati dyn Error. Trait dan trait object akan dibahas tuntas pada section Trait; untuk saat ini, anggap dyn Error sebagai lubang parkir umum untuk error apa pun.
  • Box<...> — penunjuk kepemilikan (owning pointer) yang menaruh nilai di heap: satu alamat berukuran tetap, berapa pun besar isi error di belakangnya. Kombinasi keduanya menjawab pertanyaan praktis “tipe E apa yang cocok untuk main yang bisa gagal karena seribu sebab berbeda?” — jawabannya: wadah umum berukuran tetap.

Kerja samanya dengan ? membuat notasi ini terasa ajaib. Ingat ekspansi ? pada chapter sebelumnya: cabang Err mengirim error lewat return Err(error). Untuk sampai ke tipe Box<dyn Error>, error asli seperti ParseIntError maupun pesan &str dari ok_or perlu dikonversi lebih dulu — dan di titik inilah mekanisme bawaan bahasa bergerak: ? otomatis memakai trait From untuk mengubah tipe error asli menjadi tipe error yang diminta tanda tangan fungsi. Kita tidak menulis konversi apa pun; ParseIntError masuk ke Box sebagai dyn Error begitu saja. Detail From dan trait object menjadi materi section Trait — cukup ketahui bahwa konversi otomatis inilah yang membuat Box<dyn Error> begitu nyaman.

Perilaku Runtime: Err dari main vs panic #

Jalur keluar Err dari main terlihat mirip panic pada layar — keduanya mencetak pesan dan mengakhiri program — tetapi keduanya berbeda dalam empat hal yang menentukan:

AspekErr dari mainpanic!
Awalan pesanError: ...thread 'main' (id) panicked at ...
Exit code1101
BacktraceTidak tersediaTersedia lewat RUST_BACKTRACE=1
Asal kegagalanKategori recoverable yang diputuskan berakhirBug kategori unrecoverable

Tabel di atas menegaskan pembagian peran yang kita bangun sejak chapter pertama: Err dari main adalah kegagalan terkontrol — program tahu dirinya gagal, melaporkannya dengan sopan, dan keluar dengan kode yang bisa dibaca skrip; panic adalah kecelakaan yang membawa alamat dan jejak kejadian. Alat pemanggil seperti skrip shell, alat CI, atau supervisor membedakan keduanya dengan exit code: 1 berarti “tugas gagal secara wajar”, 101 berarti “program mengalami bug”.

Ringkasan #

Fungsi main boleh mengembalikan Result<(), Box<dyn Error>> — Ok(()) menandakan sukses, Err dicetak otomatis sebagai Error: ... dan proses keluar dengan exit code 1. Notasi Box<dyn Error> menggabungkan penunjuk kepemilikan Box dengan trait object dyn Error menjadi wadah error serbaguna, dan operator ? mengonversi tipe error apa pun ke wadah tersebut secara otomatis lewat trait From. Dengan bantuan std::env::args(), nth, dan ok_or, program CLI pendaftaran kita menangani argumen hilang dan argumen rusak tanpa satu pun match. Jalur ini sempurna untuk program kecil; ketika jenis kegagalan perlu dibedakan dan membawa data konteks, saatnya mendefinisikan enum error kustom — chapter berikutnya.