Error Unrecoverable pake panic!
Kadang hal-hal buruk terjadi di kode kita, dan nggak ada yang bisa kita lakuin
buat ngatasinnya. Di kasus kayak gini, Rust punya macro panic!. Ada dua cara
buat micu sebuah panic di praktiknya: dengan ngambil aksi yang bikin kode
kita panic (kayak akses array ngelewatin akhirnya) atau dengan secara
eksplisit manggil macro panic!. Di dua kasus itu, kita nyebabin sebuah panic
di program kita. Secara default, panics ini bakal nyetak pesan kegagalan,
unwind (nggulung balik), ngebersihin stack, terus quit (keluar). Lewat
environment variable (variabel lingkungan), kita juga bisa nyuruh Rust buat
nampilin call stack pas sebuah panic terjadi biar lebih gampang buat ngelacak
sumber dari panic itu.
Unwinding the Stack atau Aborting sebagai Respon ke Panic
Secara default, pas sebuah panic terjadi, program mulai proses unwinding, yang artinya Rust jalan mundur ke atas stack terus ngebersihin data dari tiap fungsi yang dia temuin. Tapi, jalan mundur terus ngebersihin data itu butuh banyak kerjaan. Makanya, Rust ngebolehin kita milih alternatif yaitu langsung aborting (ngebatalin), yang ngeakhirin program tanpa ngebersihin apa-apa.
Memori yang tadinya dipake program terus bakal perlu dibersihin sama sistem
operasi (OS). Kalau di project kita kita butuh ngebikin file binary hasil
akhirnya sekecil mungkin, kita bisa pindah dari unwinding jadi aborting
pas terjadi panic dengan nambahin panic = 'abort' ke bagian [profile]
yang sesuai di file Cargo.toml kita. Misalnya, kalau kita mau abort pas
panic di release mode, tambahin ini:
[profile.release]
panic = 'abort'
Yuk kita coba manggil panic! di program yang simpel:
fn main() {
panic!("crash and burn");
}
Pas kita jalanin programnya, kita bakal liat yang kayak gini:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/panic`
thread 'main' panicked at src/main.rs:2:5:
crash and burn
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Pemanggilan panic! nyebabin pesan error yang ada di dua baris terakhir. Baris
pertama nunjukin pesan panic kita dan lokasi di source code kita di mana
panic-nya terjadi: src/main.rs:2:5 nunjukin kalau itu ada di baris kedua,
karakter kelima di file src/main.rs kita.
Di kasus ini, baris yang ditunjuk adalah bagian dari kode kita, dan kalau kita
buka baris itu, kita bakal liat pemanggilan macro panic!. Di kasus lain,
pemanggilan panic! mungkin ada di kode yang dipanggil sama kode kita, dan
nama file serta nomor baris yang dilaporin sama pesan error-nya bakal nunjukin
kode punya orang lain di mana macro panic! itu dipanggil, bukan baris kode
kita yang akhirnya nyebabin pemanggilan panic! itu.
Kita bisa pake backtrace dari fungsi-fungsi tempat panic! dipanggil buat
nyari tau bagian mana dari kode kita yang nyebabin masalahnya. Buat mahamin
gimana cara pake backtrace panic!, yuk kita liat contoh lain dan liat
gimana rasanya pas pemanggilan panic! dateng dari sebuah library gara-gara
ada bug di kode kita, bukannya dari kode kita yang manggil macro-nya secara
langsung. Listing 9-1 punya kode yang nyoba akses indeks di vector yang ngelewatin
rentang (range) indeks yang valid.
fn main() {
let v = vec![1, 2, 3];
v[99];
}
panic!Di sini, kita lagi nyoba akses elemen ke-100 dari vector kita (yang ada di indeks
99 karena indexing mulai dari nol), tapi vector-nya cuma punya tiga elemen. Di
situasi ini, Rust bakal panic. Pake [] seharusnya balikin sebuah elemen,
tapi kalau kita ngasih indeks yang nggak valid, nggak ada elemen yang bisa
dibalikin Rust di sini yang bener.
Di C, nyoba baca data ngelewatin akhir dari struktur data itu dianggap sebagai undefined behavior (perilaku yang nggak terdefinisi). Kita mungkin bakal dapet apa pun yang ada di lokasi memori yang harusnya sesuai sama elemen itu di struktur datanya, walaupun memori itu bukan milik struktur data tersebut. Ini disebut buffer overread dan bisa memicu celah keamanan (security vulnerabilities) kalau seorang attacker (penyerang) bisa memanipulasi indeks sedemikian rupa biar bisa baca data yang nggak boleh mereka baca yang disimpan setelah struktur data itu.
Buat ngelindungin program kita dari celah semacam ini, kalau kita nyoba baca elemen di indeks yang nggak ada, Rust bakal ngehentiin eksekusi dan nolak buat lanjut. Yuk kita coba dan liat hasilnya:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/panic`
thread 'main' panicked at src/main.rs:4:6:
index out of bounds: the len is 3 but the index is 99
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Error ini nunjuk ke baris 4 dari main.rs kita di mana kita nyoba akses indeks
99 dari vector di v.
Baris note: ngasih tau kita kalau kita bisa nge-set environment variable
RUST_BACKTRACE buat dapetin backtrace dari apa persisnya yang terjadi yang
nyebabin error itu. Sebuah backtrace adalah daftar dari semua fungsi yang
udah dipanggil sampe bisa nyampe ke titik ini. Backtraces di Rust cara
kerjanya sama kayak di bahasa lain: kunci buat baca backtrace adalah mulai
dari atas terus baca sampe kita liat file yang kita tulis sendiri. Itu adalah
titik di mana masalahnya bermula. Baris-baris di atas titik itu adalah kode yang
dipanggil sama kode kita; baris-baris di bawahnya adalah kode yang manggil kode
kita. Baris-baris sebelum dan sesudah ini mungkin nyakup kode inti Rust, kode
standard library, atau crates yang lagi kita pake. Yuk kita coba dapetin
backtrace dengan nge-set environment variable RUST_BACKTRACE ke nilai apa
pun kecuali 0. Listing 9-2 nunjukin output yang mirip sama apa yang bakal kita
liat.
$ RUST_BACKTRACE=1 cargo run
thread 'main' panicked at src/main.rs:4:6:
index out of bounds: the len is 3 but the index is 99
stack backtrace:
0: rust_begin_unwind
at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:692:5
1: core::panicking::panic_fmt
at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:75:14
2: core::panicking::panic_bounds_check
at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:273:5
3: <usize as core::slice::index::SliceIndex<[T]>>::index
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/slice/index.rs:274:10
4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/slice/index.rs:16:9
5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:3361:9
6: panic::main
at ./src/main.rs:4:6
7: core::ops::function::FnOnce::call_once
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
panic! yang ditampilin pas environment variable RUST_BACKTRACE di-setOutputnya lumayan banyak tuh! Output pasti yang bakal kita liat mungkin beda-beda
tergantung dari sistem operasi sama versi Rust yang dipake. Biar dapet
backtraces dengan informasi sebanyak ini, debug symbols harus dinyalain.
Debug symbols dinyalain secara default pas kita pake cargo build atau
cargo run tanpa flag --release, kayak yang kita lakuin di sini.
Di output di Listing 9-2, baris 6 dari backtrace-nya nunjuk ke baris di project kita yang nyebabin masalahnya: baris 4 dari src/main.rs. Kalau kita nggak mau program kita panic, kita harus mulai nyari masalahnya di lokasi yang ditunjuk sama baris pertama yang nyebutin file yang kita tulis sendiri. Di Listing 9-1, di mana kita sengaja nulis kode yang bakal panic, cara buat benerin panic-nya adalah dengan nggak minta elemen yang ada di luar rentang indeks vector-nya. Pas kode kita panic di masa depan nanti, kita harus nyari tau aksi apa yang lagi dilakuin sama kode itu pake nilai apa sampe bisa nyebabin panic dan apa yang seharusnya dilakuin sama kode itu sebagai gantinya.
Kita bakal balik lagi bahas panic! dan kapan kita harus dan nggak harus pake
panic! buat nanganin kondisi error di bagian βTo panic! or Not to
panic!β nanti di bab ini. Selanjutnya, kita bakal
liat gimana caranya buat pulih dari sebuah error pake Result.