Lewati ke konten

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.

fn main() {
panic!("ada yang salah besar");
}
thread 'main' (12345) panicked at src/main.rs:2:5:
ada yang salah besar
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

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

▶ Coba sendiri

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));
}
▶ Coba sendiri
fn main() {
let value: Option<i32> = None;
println!("{}", value.unwrap()); // panic: called `Option::unwrap()` on a `None` value
}
▶ Coba sendiri
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));
}
▶ Coba sendiri

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.

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 Result
fn 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: panic
fn 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}");
}
▶ Coba sendiri

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 kondisinya false.
  • assert_eq!(left, right) panic kalau kedua nilai tidak sama, dan mencetak keduanya.
  • unreachable!("pesan") menandai kode yang seharusnya tidak pernah dijalankan, seperti cabang match untuk 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: 80
hari ke-3 adalah Rabu
▶ Coba sendiri

Semua 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: 80

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

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 8080
▶ Coba sendiri

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 30
▶ Coba sendiri
  • panic! menghentikan program seketika dengan sebuah pesan error.
  • Beberapa operasi standar (indeks array yang salah, .unwrap() pada None, pembagian dengan nol) otomatis panic.
  • Pakai panic! untuk bug atau invarian yang seharusnya mustahil dilanggar; pakai Result untuk kegagalan yang wajar bisa ditangani pemanggil.
  • .expect("pesan") mirip .unwrap(), tapi dengan pesan yang membuat panic lebih mudah di-debug.
  • assert!, assert_eq!, dan unreachable! sengaja panic saat sesuatu yang kamu yakini benar ternyata salah.

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