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:
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:
rustc --edition 2021 main.rs -o pendaftaran
./pendaftaran 34
echo "Exit code: $?"
./pendaftaran abc
echo "Exit code: $?"
./pendaftaran
echo "Exit code: $?"Output yang Dihasilkan:
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: 1Mari kita bedah kode dan hasilnya:
- Baris 1 (
use std::error::Error;): import traitErrordari pustaka standar agar penulisanBox<dyn Error>di tanda tangan cukup pendek. - Baris 3 (
fn main() -> Result<(), Box<dyn Error>>): tanda tangan barumain. Tipe suksesnya adalah()— unit type, nilai “tidak ada apa-apa” — karena program yang berhasil tidak perlu mengembalikan data; cukup menyatakan sukses lewatOk(()). - 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 menghasilkanOption<String>karena argumen tersebut bisa tidak ada. Methodok_or(pesan)mengubahOptionmenjadiResult:SomemenjadiOk,NonemenjadiErr(pesan). Operator?kemudian mempropagasiErrkeluar darimainsaat argumen hilang. - Baris 8 (
let usia: u32 = masukan.parse()?;): pola lama dari chapter Operator Tanya; teks"abc"gagal diparse dan error-nya langsung keluar darimain. - Baris 11 (
Ok(())): nilai kembalian jalur sukses —Okmembawa unit(). Baris ini wajib ada karenamainkini berjanji mengembalikanResult. - Pesan
Error: ...pada output: kedua jalur kegagalan dicetak otomatis oleh runtime Rust — kita tidak menulis satu punprintln!untuk error. IsiErrditampilkan dengan format{:?}: error parsing tampil sebagai struct mentah, pesanok_ortampil sebagai string berkutip. - Exit code: jalur sukses menghasilkan
0, kedua jalurErrmenghasilkan1— 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 traitError: satu nama tipe yang mewakili “semua tipe error yang mengimplementasikan traitError”.ParseIntError, error I/O dari berkas, hingga tipe error buatan kita sendiri — semuanya sah menempatidyn Error. Trait dan trait object akan dibahas tuntas pada section Trait; untuk saat ini, anggapdyn Errorsebagai 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 “tipeEapa yang cocok untukmainyang 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:
| Aspek | Err dari main | panic! |
|---|---|---|
| Awalan pesan | Error: ... | thread 'main' (id) panicked at ... |
| Exit code | 1 | 101 |
| Backtrace | Tidak tersedia | Tersedia lewat RUST_BACKTRACE=1 |
| Asal kegagalan | Kategori recoverable yang diputuskan berakhir | Bug 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.