Panic di Rust
Panic adalah cara Rust menghentikan program seketika saat terjadi sesuatu yang salah dan tidak bisa dipulihkan. Kalau kamu mengikuti halaman-halaman sebelumnya, sebenarnya kamu mungkin sudah memicu beberapa panic tanpa sengaja: mengakses indeks melewati ujung array, atau memanggil .unwrap() pada None, keduanya menyebabkan panic.
Memicu panic secara manual
Section titled “Memicu panic secara manual”fn main() { panic!("ada yang salah besar");}thread 'main' (12345) panicked at src/main.rs:2:5:ada yang salah besarnote: run with `RUST_BACKTRACE=1` environment variable to display a backtraceAngka di dalam tanda kurung, di sini (12345), adalah ID thread-nya. Kamu akan melihat angka yang berbeda setiap kali program dijalankan, dan versi Rust yang lebih lama tidak menampilkannya. Baris terakhir (dari Rust, dalam bahasa Inggris) memberi tahu bahwa kamu bisa menjalankan program dengan variabel environment RUST_BACKTRACE=1 untuk melihat backtrace.
Saat dijalankan, program mencetak pesan itu lalu berhenti. Tidak ada kode setelah panic! yang dieksekusi.
Hal-hal yang panic tanpa kamu memanggil panic! langsung
Section titled “Hal-hal yang panic tanpa kamu memanggil panic! langsung”Beberapa operasi yang sudah kamu kenal akan panic dengan sendirinya kalau ada yang salah. Setiap contoh di bawah membaca nilai berisikonya lewat parameter fungsi, bukan dari literal yang ditulis langsung. Kalau indeksnya atau angka 0-nya ditulis langsung di kode, compiler sering bisa tahu saat kompilasi bahwa operasinya pasti gagal, lalu menolak membangun program sama sekali, alih-alih membiarkannya berjalan lalu panic.
fn get(numbers: &[i32], index: usize) -> i32 { numbers[index] // panic: indeks di luar batas}
fn main() { let numbers = [1, 2, 3]; println!("{}", get(&numbers, 10));}fn main() { let value: Option<i32> = None; println!("{}", value.unwrap()); // panic: called `Option::unwrap()` on a `None` value}fn divide(a: i32, b: i32) -> i32 { a / b // panic: attempt to divide by zero}
fn main() { let a = 10; let b = 0; println!("{}", divide(a, b));}Pesan panic dari Rust sendiri tetap dalam bahasa Inggris: called Option::unwrap() on a None value artinya unwrap() dipanggil pada nilai None, dan attempt to divide by zero artinya ada percobaan membagi dengan nol. Setiap contoh ini adalah cara Rust menolak membiarkan programmu lanjut dalam keadaan yang tidak masuk akal, alih-alih diam-diam mengembalikan nilai sampah.
panic! vs Result
Section titled “panic! vs Result”Inilah keputusan sesungguhnya yang akan sering kamu ambil: apakah kegagalan ini sebaiknya menghentikan program, atau pemanggil diberi kesempatan untuk menanganinya?
panic!: untuk bug dan situasi yang seharusnya tidak pernah terjadi kalau kodenya benar. Tidak ada cara masuk akal untuk pulih, jadi berhenti seketika (sebelum kerusakannya bertambah) adalah pilihan paling aman.Result(dibahas di halaman sebelumnya): untuk kegagalan yang wajar diduga dan ingin ditangani oleh pemanggil, seperti input user yang salah, file yang tidak ada, atau request jaringan yang gagal.
// Input yang salah dari user: bisa dipulihkan, pakai Resultfn parse_age(input: &str) -> Result<u32, String> { input.parse().map_err(|_| String::from("bukan umur yang valid"))}
// Sebuah invarian yang mustahil dilanggar kalau kodenya benar: panicfn get_first(numbers: &[i32]) -> i32 { if numbers.is_empty() { panic!("get_first dipanggil dengan slice kosong"); } numbers[0]}unwrap() dan expect() adalah jalan pintas menuju panic
Section titled “unwrap() dan expect() adalah jalan pintas menuju panic”Kamu sudah melihat .unwrap() pada Option maupun Result. Sebenarnya ia hanya jalan pintas untuk “berikan nilainya, atau panic kalau tidak ada”. .expect("pesan") melakukan hal yang sama, tapi memungkinkanmu menambahkan pesan yang lebih berguna:
fn main() { let input = "42"; let number: i32 = input.parse().expect("input seharusnya angka yang valid"); println!("{number}");}Kalau parse gagal, pesan panic-nya akan memuat teksmu, sehingga debugging jauh lebih mudah dibandingkan pesan umum yang diberikan .unwrap().
Memeriksa asumsimu: assert!, assert_eq! dan unreachable!
Section titled “Memeriksa asumsimu: assert!, assert_eq! dan unreachable!”Kadang kamu tahu sesuatu pasti benar di titik tertentu dalam kodemu. Rust punya beberapa makro kecil yang mengubah keyakinan itu menjadi pemeriksaan. Kalau pemeriksaannya gagal, makro itu panic dengan pesan yang jelas, jadi bug langsung ketahuan di tempat asalnya, bukan baru menimbulkan hasil aneh belakangan.
assert!(condition, "pesan")panic kalau kondisinyafalse.assert_eq!(left, right)panic kalau kedua nilai tidak sama, dan mencetak keduanya.unreachable!("pesan")menandai kode yang seharusnya tidak pernah dijalankan, seperti cabangmatchuntuk nilai yang mustahil muncul.
fn average(numbers: &[i32]) -> i32 { assert!(!numbers.is_empty(), "average butuh minimal satu angka");
let mut total = 0; for n in numbers { total += n; } total / numbers.len() as i32}
fn day_name(day: u8) -> &'static str { match day { 1 => "Senin", 2 => "Selasa", 3 => "Rabu", 4 => "Kamis", 5 => "Jumat", 6 => "Sabtu", 7 => "Minggu", _ => unreachable!("hari harus 1 sampai 7, tapi dapat {day}"), }}
fn main() { let avg = average(&[70, 80, 90]); assert_eq!(avg, 80); println!("rata-rata: {avg}");
println!("hari ke-3 adalah {}", day_name(3));}Output:
rata-rata: 80hari ke-3 adalah RabuSemua pemeriksaan lolos di sini, jadi program berjalan normal. Sekarang ubah daftarnya menjadi [70, 80, 95] di playground. Rata-ratanya menjadi 81, dan assert_eq! menghentikan program sambil menampilkan kedua nilai berdampingan:
thread 'main' (12345) panicked at src/main.rs:26:5:assertion `left == right` failed left: 81 right: 80Pesan assertion left == right failed artinya pemeriksaan “kiri sama dengan kanan” gagal. Coba juga average(&[]) atau day_name(9), dan kamu akan melihat pesanmu sendiri di dalam panic.
Pemeriksaan ini selalu dijalankan, bahkan di build release, jadi buatlah tetap ringan. Kamu akan bertemu assert_eq! lagi di halaman Testing, di mana makro ini menjadi cara utama sebuah test menyatakan “ini harus sama dengan itu”. Ada juga todo!(), yang panic dengan pesan “not yet implemented” (belum diimplementasikan). Makro ini praktis sebagai isi sementara saat kamu merancang fungsi yang baru akan kamu selesaikan nanti.
Latihan
Section titled “Latihan”1. Nomor port dengan expect
Section titled “1. Nomor port dengan expect”Kamu punya nilai konfigurasi let port_text = "8080";. Ubah menjadi u16 dengan .parse() dan .expect(...), memakai pesan yang menjelaskan apa yang salah. Lalu cetak Mendengarkan di port 8080. Petunjuk: variabelnya butuh tipe, let port: u16 = ..., supaya parse tahu harus menghasilkan apa. Setelah itu, ubah teksnya menjadi "80a0" untuk melihat pesanmu di dalam panic.
Lihat solusi
fn main() { let port_text = "8080"; let port: u16 = port_text .parse() .expect("port di konfigurasi seharusnya angka dari 0 sampai 65535"); println!("Mendengarkan di port {port}");}Output:
Mendengarkan di port 80802. Patungan bayar tagihan
Section titled “2. Patungan bayar tagihan”Tulis fn split_bill(total: u32, people: u32) -> u32 yang mengembalikan bagian setiap orang. Membagi tagihan ke nol orang adalah bug di kode pemanggil, jadi pakai assert! dengan pesan yang jelas untuk menangkapnya. Cetak bagian per orang untuk total 120 dan 4 orang. Petunjuk: taruh assert! di baris pertama fungsi, sebelum pembagian.
Lihat solusi
fn split_bill(total: u32, people: u32) -> u32 { assert!(people > 0, "split_bill butuh minimal satu orang"); total / people}
fn main() { let share = split_bill(120, 4); println!("Setiap orang membayar {share}");}Output:
Setiap orang membayar 30Ringkasan
Section titled “Ringkasan”panic!menghentikan program seketika dengan sebuah pesan error.- Beberapa operasi standar (indeks array yang salah,
.unwrap()padaNone, pembagian dengan nol) otomatis panic. - Pakai
panic!untuk bug atau invarian yang seharusnya mustahil dilanggar; pakaiResultuntuk kegagalan yang wajar bisa ditangani pemanggil. .expect("pesan")mirip.unwrap(), tapi dengan pesan yang membuat panic lebih mudah di-debug.assert!,assert_eq!, danunreachable!sengaja panic saat sesuatu yang kamu yakini benar ternyata salah.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Apa yang terjadi saat kamu memanggil panic!?
2. Menurut pegangan di halaman ini, kapan sebaiknya kamu memakai panic! alih-alih Result?
3. Apa beda .unwrap() dan .expect('pesan')?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- Unrecoverable Errors with panic! — The Rust Book (bahasa Inggris): apa yang terjadi saat panic, unwinding, dan cara membaca backtrace.
- To panic! or Not to panic! — The Rust Book (bahasa Inggris): panduan resmi untuk memilih antara
panic!danResult, lengkap dengan contoh. - panic! — Rust by Example (bahasa Inggris): contoh singkat sebuah panic, yang menunjukkan bahwa memori tetap dibebaskan saat program melakukan unwinding.
assert_eq!— dokumentasi std (bahasa Inggris): cara kerja makro assertion, termasuk pesan kustom.panic!— dokumentasi std (bahasa Inggris): referensi untukpanic!itu sendiri, hubungannya denganunwrap, dan kapan panic adalah alat yang tepat.