Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

Filename: src/main.rs
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.

Filename: src/main.rs
fn main() {
    let v = vec![1, 2, 3];

    v[99];
}
Listing 9-1: Nyoba akses elemen yang ngelewatin akhir sebuah vector, yang bakal nyebabin pemanggilan 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.
Listing 9-2: Backtrace yang dihasilin oleh pemanggilan panic! yang ditampilin pas environment variable RUST_BACKTRACE di-set

Outputnya 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.