Mekanisme exception menyediakan jalur pelaporan error yang terpisah dari nilai balik fungsi biasa. Topik ini membahas pernyataan raise untuk melontarkan exception, klausa rescue untuk menangkapnya, klausa else dan ensure untuk memisahkan jalur berhasil dari jalur pembersihan, serta penyusunan hierarki exception khusus untuk kebutuhan program. Setelah membaca topik ini, kegagalan operasi dapat dilaporkan dengan tipe exception yang tepat dan program tetap dapat melanjutkan eksekusinya.
Menangkap exception dengan rescue#
Struktur begin dan rescue membungkus kode yang berpotensi gagal. Pernyataan raise melontarkan sebuah exception, yaitu objek yang membawa informasi kegagalan, dan klausa rescue menangkap objek itu sehingga program tidak berhenti. Objek yang ditangkap diterima sebuah variabel pada klausa, dan properti message pada objek tersebut berisi uraian kegagalannya.
begin
raise ArgumentError.new("nilai di luar rentang")
rescue e : ArgumentError
puts e.message
end
begin
raise "kegagalan tak terduga"
rescue e
puts e.message
endnilai di luar rentang
kegagalan tak terdugaPernyataan raise menerima instance dari class Exception atau turunannya, misalnya ArgumentError. Bentuk singkat raise diikuti string juga sah, dan Crystal membungkus string itu menjadi instance Exception baru. Pada kedua bentuk, properti message berisi teks yang diberikan. Selain message, objek exception juga menyediakan backtrace berupa daftar posisi pemanggilan yang mengarah ke lokasi pelontaran.
Klausa rescue dapat menyebut tipe tertentu sehingga hanya exception dengan tipe itu yang ditangkap. Klausa rescue tanpa tipe menangkap semua exception dan berperan sebagai penangkap umum. Klausa-klausa tersebut diperiksa berurutan dari atas ke bawah, dan klausa pertama yang tipenya cocok digunakan.
begin
raise KeyError.new("kunci tidak ada")
rescue e : ArgumentError
puts "argumen salah"
rescue e
puts "ditangkap umum: #{e.message}"
endditangkap umum: kunci tidak adaContoh di atas melontarkan KeyError, sedangkan klausa pertama hanya menangkap ArgumentError. Karena tidak cocok, pemeriksaan berlanjut ke klausa kedua yang tanpa tipe, sehingga pesan tampil melalui jalur penangkap umum.
Apabila tidak ada klausa rescue yang cocok, exception naik ke pemanggil di atasnya. Apabila tidak ada satu klausa pun yang menangkap sampai batas terluar program, eksekusi berhenti dengan pesan Unhandled exception diikuti uraian kegagalannya. Menangkap exception pada lapisan yang tepat menjaga program tetap hidup sekaligus mempertahankan informasi kegagalan.
Hierarki exception khusus#
Semua exception pada Crystal mewarisi class Exception. Program dapat menambah tipe kegagalan sendiri dengan membuat class turunan, baik langsung dari Exception maupun dari sebuah class perantara yang mengelompokkan kegagalan sejenis. Hierarki semacam ini membuat klausa rescue dapat memilih antara menangkap satu kegagalan spesifik atau seluruh kelompoknya.
Class exception khusus biasanya menerima pesan pada def initialize dan meneruskannya ke class induk melalui super. Data tambahan tentang kegagalan disimpan sebagai properti biasa sehingga pemanggil rescue dapat membacanya. Program hierarki-stok.cr berikut menggunakan pola tersebut untuk operasi stok gudang.
# Hierarki exception khusus untuk operasi stok gudang.
# Jalankan dengan: crystal hierarki-stok.cr
class StokError < Exception
end
class StokKurang < StokError
getter kekurangan : Int32
def initialize(pesan : String, @kekurangan : Int32)
super(pesan)
end
end
class StokHabis < StokError
end
def ambil_barang(stok : Int32, jumlah : Int32)
raise StokHabis.new("stok habis") if stok == 0
raise StokKurang.new("stok kurang #{jumlah - stok}", jumlah - stok) if jumlah > stok
stok - jumlah
end
permintaan = [{3, 2}, {3, 5}, {0, 1}]
permintaan.each do |(stok, jumlah)|
begin
puts "sisa stok: #{ambil_barang(stok, jumlah)}"
rescue e : StokKurang
puts "ditolak: #{e.message} (kekurangan #{e.kekurangan})"
rescue e : StokError
puts "ditolak: #{e.message}"
end
end# menjalankan program hierarki stok
crystal hierarki-stok.crsisa stok: 1
ditolak: stok kurang 2 (kekurangan 2)
ditolak: stok habisProgram di atas menetapkan class perantara StokError dengan dua turunannya. Class StokKurang membawa properti kekurangan berisi selisih permintaan terhadap stok. Klausa rescue untuk StokKurang diletakkan di atas klausa untuk StokError karena pemeriksaan berhenti pada kecocokan pertama. Permintaan pertama berhasil tanpa exception, permintaan kedua jatuh ke klausa StokKurang beserta propertinya, dan permintaan ketiga yang berupa StokHabis ditangkap klausa StokError. Dengan hierarki seperti ini, satu klausa rescue e : StokError mampu menangkap semua kegagalan stok tanpa menuliskan setiap tipenya satu per satu.
Klausa else dan ensure#
Klausa ensure memuat pembersihan yang wajib dijalankan baik terjadi exception maupun tidak. Blok begin diproses terlebih dahulu, exception apabila ada ditangkap rescue, dan ensure tetap dieksekusi paling akhir. Pola ini cocok untuk menutup berkas, membebaskan sumber daya, atau mencatat log penutupan. Klausa else melengkapi rescue dan hanya dijalankan ketika tidak ada exception yang ditangkap, sehingga jalur berhasil, jalur gagal, dan jalur pembersihan terpisah secara eksplisit. Ketika keempat klausa digunakan sekaligus, urutannya tetap, yaitu blok begin, rescue, else, dan diakhiri ensure.
begin
puts "mencoba"
rescue e
puts "gagal: #{e.message}"
else
puts "berhasil tanpa exception"
endmencoba
berhasil tanpa exceptionProgram penangan-dasar.cr berikut merangkum raise, rescue, ensure, dan nilai balik begin dalam satu program yang dapat dijalankan.
# Demonstrasi raise, rescue, dan ensure.
# Jalankan dengan: crystal penangan-dasar.cr
def bagi(a : Int32, b : Int32) : Int32
raise ArgumentError.new("pembagi tidak boleh nol") if b == 0
a // b
end
# begin/rescue adalah ekspresi: nilainya bisa diassign
hasil = begin
bagi(10, 0)
rescue e : ArgumentError
0
end
puts "hasil pembagian: #{hasil}"
# rescue tanpa tipe menangkap semua exception
begin
raise "kegagalan tak terduga"
rescue e
puts "ditangkap: #{e.message}"
end
# ensure selalu dijalankan
begin
puts "membuka berkas"
puts "memproses data"
ensure
puts "menutup berkas"
end# menjalankan program penanganan dasar
crystal penangan-dasar.crhasil pembagian: 0
ditangkap: kegagalan tak terduga
membuka berkas
memproses data
menutup berkasFungsi bagi melontarkan ArgumentError ketika pembaginya nol. Ekspresi begin hingga rescue pada penugasan variabel hasil bernilai 0 karena jalur rescue yang dieksekusi, dan nilai itu langsung dicetak. Bagian kedua memperlihatkan penangkap umum yang menangkap exception berbentuk string. Bagian ketiga tidak melontarkan exception apa pun, tetapi pesan menutup berkas dari klausa ensure tetap tampil.
Kesalahan umum#
- Menulis
retryuntuk mengulang blokbegin. Pernyataan itu tersedia di bahasa lain, tetapi Crystal tidak memilikinya sehingga compiler menolak programnya. Ulangan dilakukan dengan perulanganwhileyang memanggil kembali operasi di dalambegindan mengubah kondisinya pada klausarescue. - Menangkap semua exception dengan klausa tanpa tipe pada setiap panggilan. Kebiasaan ini menyembunyikan bug yang seharusnya terlihat, misalnya kesalahan logika yang terbungkus pesan umum. Tangkap tipe selengkap mungkin dan letakkan penangkap umum hanya pada batas terluar program.
- Menempatkan penangkap umum di atas klausa bertipe. Kecocokan pertama selalu menang, sehingga klausa bertipe di bawahnya tidak pernah terjangkau. Urutan yang benar adalah klausa paling spesifik di atas dan penangkap umum paling bawah.
Ringkasan#
- Pernyataan
raisemelontarkan instanceExceptionatau turunannya, dengan bentuk singkatraise "pesan"untuk string. - Klausa
rescuemenangkap exception berdasarkan tipe secara berurutan dari atas ke bawah, dan klausa tanpa tipe menjadi penangkap umum. - Klausa
elsehanya dijalankan ketika tidak ada exception, sedangkanensureselalu dijalankan untuk pembersihan. - Struktur
beginhinggarescueadalah ekspresi yang nilainya dapat ditugaskan, dan class exception khusus dibuat sebagai turunanExceptionyang dapat membawa data tambahan.
Referensi#
- Dokumentasi bahasa Crystal 1.22 — Crystal
— menjelaskan sintaks exception meliputi
raise,rescue,else, danensure. - Referensi API Crystal 1.22.1 — Crystal
— merinci class
Exceptionbeserta propertimessagedanbacktraceserta exception bawaan sepertiArgumentErrordanKeyError.