Topik ini membahas panic! sebagai mekanisme kegagalan yang mengakhiri program, serta kriteria pemilihannya dibandingkan Result yang memelihara jalur pemulihan. Pembahasan mencakup bentuk pesan panic beserta lokasinya, proses unwinding yang membersihkan memori saat program berhenti, penyebab panic dari pemeriksaan runtime seperti pengindeksan di luar batas, dan panduan praktis kapan kegagalan sebaiknya dinyatakan mustahil dipulihkan. Setelah membaca topik ini, keputusan antara menggugurkan program dan mengembalikan Err dapat diambil dengan alasan yang eksplisit, dan pesan panic yang muncul dapat dibaca sampai ke sumber kejadiannya.
Apa yang terjadi saat panic#
Macro panic! menghentikan alur program pada baris pemanggilannya. Sebelum program benar-benar berakhir, Rust mencetak pesan yang memuat nama thread, lokasi berkas, baris, dan kolom pemanggilan, serta pesan yang menjadi argumen macro tersebut. Program berikut memanggil panic! di tengah pencetakan agar urutan keluarannya terlihat.
export PATH="$HOME/.cargo/bin:$PATH"
# buat program yang memanggil macro panic!
printf 'fn main() {\n println!("sebelum panic");\n panic!("suhu sensor melebihi batas");\n}\n' > panik-sensor.rs
# kompilasi lalu jalankan, nomor thread pada pesan bervariasi sehingga disaring
rustc --edition=2021 panik-sensor.rs -o panik-sensor && ./panik-sensor 2>&1 | sed -E 's/ \([0-9]+\)//' | sed -n '1,5p'sebelum panic
thread 'main' panicked at panik-sensor.rs:3:5:
suhu sensor melebihi batas
note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceBaris sebelum panic tercetak lebih dahulu karena macro panic! baru dijalankan pada baris ketiga. Pesan panic kemudian memuat lokasi panik-sensor.rs:3:5, yaitu posisi pemanggilan macro itu sendiri, diikuti pesan suhu sensor melebihi batas. Baris terakhir keluaran adalah catatan mengenai variabel lingkungan RUST_BACKTRACE untuk menampilkan jejak tumpukan. Nomor thread yang sebenarnya tercetak setelah kata main berbeda pada setiap jalankan sehingga disaring dari contoh, dan program berakhir dengan kode keluar 101.
Penyetelan RUST_BACKTRACE=1 mengganti catatan itu dengan daftar bingkai tumpukan yang menunjukkan rantai pemanggilan sampai ke titik panic.
export PATH="$HOME/.cargo/bin:$PATH"
# jalankan program yang sama dengan jejak tumpukan aktif
RUST_BACKTRACE=1 ./panik-sensor 2>&1 | sed -E 's/ \([0-9]+\)//' | sed -n '1,13p'sebelum panic
thread 'main' panicked at panik-sensor.rs:3:5:
suhu sensor melebihi batas
stack backtrace:
0: __rustc::rust_begin_unwind
at /rustc/58cf8f9c35b355199727627d07ae34a4e2846d67/library/std/src/panicking.rs:679:5
1: core::panicking::panic_fmt
at /rustc/58cf8f9c35b355199727627d07ae34a4e2846d67/library/core/src/panicking.rs:80:14
2: panik_sensor::main
3: <fn() as core::ops::function::FnOnce<()>>::call_once
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.Jejak tersebut menunjukkan bahwa panic berasal dari panik_sensor::main setelah melewati fungsi pembantu di dalam pustaka standar. Bingkai dengan awalan __rustc atau core berasal dari pustaka standar, sedangkan bingkai yang bernama program sendiri menandai kode yang ditulis pengembang. Nilai full pada RUST_BACKTRACE=full menampilkan jejak yang lebih rinci beserta alamat memori, tetapi untuk mencari lokasi panic pada program sendiri, mode 1 biasanya sudah memadai.
Panic juga dapat timbul tanpa macro panic!, yaitu dari pemeriksaan yang dijalankan runtime ketika program hendak melakukan operasi yang tidak aman. Pengindeksan array dengan tanda kurung siku termasuk operasi yang diawasi pemeriksaan tersebut, dan contoh berikut memakai indeks yang dihitung saat program berjalan sehingga nilainya berada di luar panjang array.
export PATH="$HOME/.cargo/bin:$PATH"
# buat program yang mengakses indeks di luar batas array
printf 'fn main() {\n let suhu = [21, 24, 19];\n let indeks = suhu.len() + 7;\n println!("{}", suhu[indeks]);\n}\n' > indeks-suhu.rs
# kompilasi lalu jalankan untuk melihat panic dari pemeriksaan batas
rustc --edition=2021 indeks-suhu.rs -o indeks-suhu && ./indeks-suhu 2>&1 | sed -E 's/ \([0-9]+\)//' | sed -n '1,4p'thread 'main' panicked at indeks-suhu.rs:4:20:
index out of bounds: the len is 3 but the index is 10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtracePesan index out of bounds menyebut panjang array dan nilai indeks yang diminta, sehingga penyebabnya dapat diperbaiki tanpa menebak. Alternatif yang aman terhadap batas adalah metode get, yang mengembalikan Option alih-alih memicu panic, dan memilih di antara keduanya berarti memilih antara menggugurkan program dan menangani ketidakhadiran nilai. Setelah panic terjadi, pembersihan memori dijalankan melalui unwinding, yaitu penelusuran tumpukan dari titik panic ke arah luar sambil menjalankan drop bagi setiap nilai yang masih hidup. Berkas yang terbuka ditutup dan memori heap dikembalikan selama proses tersebut, kemudian program berakhir dengan kode keluar 101. Konfigurasi Cargo dapat mengubah perilaku ini menjadi penghentian langsung tanpa unwinding melalui panic = "abort" pada profil tertentu, yang dipakai untuk memperkecil ukuran binary.
panic! untuk kondisi tak terpulihkan#
Panic dipilih ketika kegagalan menandakan program yang salah, bukan dunia luar yang sedang berbeda dari biasanya. Pelanggaran invarian internal adalah contohnya, yaitu sebuah fungsi yang menjanjikan hanya menerima nilai pada rentang tertentu tetapi menerima nilai di luar rentang itu, sehingga kelanjutan perhitungan tidak bermakna. Kegagalan semacam itu tidak dapat dipulihkan oleh kode yang sama karena koreksinya berada pada kode yang salah menuliskan nilai tersebut. Data yang telah rusak juga lebih baik dihentikan daripada ditularkan ke berkas atau jaringan karena kelanjutannya hanya menyebarkan kerusakan.
Contoh dan prototipe menempati posisi khusus. Buku resmi Rust menyatakan bahwa unwrap, expect, dan panic! wajar dipakai ketika tujuan kode sekadar memperlihatkan cara kerja, termasuk pada pengujian yang memang seharusnya gagal total bila kondisinya tidak terpenuhi. Pada contoh semacam itu, penanganan penuh hanya mengaburkan pokok yang sedang didemonstrasikan. Begitu kode bergerak menuju jalur data nyata, titik panic yang tidak disengaja perlu dicari dan diganti dengan penanganan yang sesungguhnya.
Result untuk kegagalan yang diharapkan#
Kegagalan yang dapat diramalkan sebaiknya tidak menjadi panic. Masukan pengguna yang salah format, berkas konfigurasi yang belum ada, dan jaringan yang putus adalah kejadian normal ketika program berjalan, bukan bug. Tipe Result menyatakan kejadian itu sebagai nilai yang dapat diperiksa, sehingga pemanggil tetap memegang kendali untuk memberi nilai cadangan, mencoba ulang, atau berhenti dengan pesan yang layak. Tabel dan diagram berikut merangkum perbedaan kedua jalur tersebut.
| Aspek | panic! | Result |
|---|---|---|
| makna kegagalan | program salah atau invarian rusak | kejadian yang diharapkan terjadi |
| penanganan | tidak ada, program berhenti | diputuskan oleh pemanggil |
| bentuk keluaran | pesan, lokasi, dan unwinding | nilai Err yang dapat diperiksa |
| contoh kejadian | indeks di luar batas, invarian rusak | teks salah format, berkas tidak ada |
Diagram tersebut menyandingkan kedua jalur dari pemicunya sampai akibat akhirnya. Jalur atas berawal dari kondisi tak terpulihkan yang berakhir pada penghentian program dengan kode keluar 101, sedangkan jalur bawah berawal dari kegagalan yang diharapkan dan berujung pada keputusan pemanggil, baik memberi nilai cadangan, mencoba lagi, maupun berhenti.
Pemilihan dapat diarahkan dengan satu uji sederhana, yaitu menentukan pihak yang mampu merespons kegagalan itu. Bila hanya penulis kode yang dapat memperbaiki keadaannya, panic memperpendek jalan menuju perbaikan karena gejalanya langsung terlihat. Bila pihak yang mampu merespons adalah pengguna akhir atau lingkungan eksternal, Result menyerahkan pilihan kepada pihak tersebut. Sebuah pustaka pembaca berkas konfigurasi sebaiknya mengembalikan Result karena isi berkas adalah urusan pemanggilnya, sedangkan pemeriksaan internal yang menemukan keadaan mustahil boleh panic karena pemanggil tidak dapat berbuat apa pun terhadap keadaan tersebut.
Rust menyediakan std::panic::catch_unwind untuk menangkap efek panic pada batas tertentu, misalnya pada kerangka pengujian atau pada kode yang menjalankan modul dari pihak lain. Alat ini bukan pengganti Result karena panic tetap dimaksudkan sebagai keadaan luar biasa, dan pemulihan lewat penangkapan tidak menjamin data yang sedang diolah tetap konsisten. Kegagalan yang diharapkan sejak awal dinyatakan sebagai Result, bukan dijadikan panic yang kemudian ditangkap.
Kesalahan umum#
Kesalahan pertama adalah memakai panic untuk kegagalan yang diharapkan, misalnya menggugurkan program karena berkas konfigurasi belum ada pada saat program dijalankan pertama kali. Pengguna kehilangan kesempatan membuat berkas tersebut, padahal kegagalan semacam ini adalah kasus klasik Result. Kembalikan error yang menjelaskan berkas yang dicari, lalu biarkan pemanggil memutuskan langkah berikutnya.
Kesalahan kedua adalah membiarkan unwrap tinggal di dalam pustaka. Pustaka tidak mengetahui invarian pemanggilnya, sehingga None atau Err yang wajar bagi pemanggil berubah menjadi crash pada program orang lain. Kembalikan Option atau Result dari API publik, dan simpan expect hanya untuk keadaan yang benar-benar mustahil menurut kontrak pustaka itu sendiri.
Kesalahan ketiga adalah mengharapkan panic dapat dicoba layaknya exception. Bahasa dengan mekanisme try dan catch menjadikan aliran exception sebagai bagian kendali alur biasa, sedangkan Rust memisahkan jalur itu secara tegas. Membangun alur kerja yang bergantung pada panic yang ditangkap catch_unwind menghasilkan kode yang rapuh terhadap perubahan versi dan sulit dibaca. Nyatakan kegagalan yang diharapkan sebagai Result sejak awal.
Ringkasan#
panic!menghentikan program dengan pesan, lokasi, pembersihan memori lewat unwinding, dan kode keluar 101.- Panic juga timbul dari pemeriksaan runtime seperti pengindeksan di luar batas, dan jejaknya dapat diperluas dengan
RUST_BACKTRACE. panic!dipilih untuk pelanggaran invarian dan kondisi mustahil dipulihkan, sedangkanResultuntuk kegagalan yang diharapkan seperti masukan salah atau berkas tidak ada.- Contoh dan prototipe boleh memakai
unwrapdanexpect, tetapi jalur data nyata menuntut keduanya diganti dengan penanganan yang eksplisit.
Referensi#
- Unrecoverable Errors with panic! — The Rust Programming Language — Mekanisme panic, unwinding, dan RUST_BACKTRACE.
- To panic! or Not to panic! — The Rust Programming Language — Panduan pemilihan antara panic dan Result dalam praktik.
- panic — Rust standard library — Dokumentasi macro panic! beserta contoh pemakaiannya.