Ruby QuickRef dari Ryan Davis berkata (tanpa penjelasan):
Jangan menyelamatkan Exception. PERNAH. atau aku akan menusukmu.
Kenapa tidak? Apa hal yang benar untuk dilakukan?
ruby
exception-handling
John
sumber
sumber
Jawaban:
TL; DR : Gunakan
StandardError
sebagai ganti untuk menangkap pengecualian umum. Saat pengecualian asli dimunculkan kembali (mis. Saat menyelamatkan hanya untuk mencatat pengecualian), penyelamatanException
mungkin tidak apa-apa.Exception
adalah akar dari hirarki pengecualian Ruby , jadi ketika Andarescue Exception
Anda menyelamatkan dari segala sesuatu , termasuk subclass sepertiSyntaxError
,LoadError
, danInterrupt
.Menyelamatkan
Interrupt
mencegah pengguna CTRLCuntuk keluar dari program.Menyelamatkan
SignalException
mencegah program merespon dengan benar terhadap sinyal. Ini tidak akan dapat dikerjakan kecuali olehkill -9
.Menyelamatkan
SyntaxError
berarti bahwaeval
yang gagal akan melakukannya secara diam-diam.Semua ini dapat ditampilkan dengan menjalankan program ini, dan mencoba CTRLCatau
kill
melakukannya:Menyelamatkan dari
Exception
bahkan bukan default. Perbuatantidak menyelamatkan dari
Exception
, itu menyelamatkan dariStandardError
. Anda biasanya harus menentukan sesuatu yang lebih spesifik daripada standarStandardError
, tetapi menyelamatkan dariException
memperluas ruang lingkup daripada mempersempitnya, dan dapat memiliki hasil bencana dan membuat perburuan bug sangat sulit.Jika Anda memiliki situasi di mana Anda ingin menyelamatkan
StandardError
dan Anda membutuhkan variabel dengan pengecualian, Anda dapat menggunakan formulir ini:yang setara dengan:
Salah satu dari beberapa kasus umum yang harus diselamatkan
Exception
adalah untuk tujuan logging / pelaporan, dalam hal ini Anda harus segera mengajukan kembali pengecualian:sumber
Throwable
di javaADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
dan kemudianrescue *ADAPTER_ERRORS => e
The nyata aturan adalah: Jangan membuang pengecualian. Objektivitas penulis kutipan Anda dipertanyakan, sebagaimana dibuktikan oleh fakta bahwa itu berakhir dengan
Tentu saja, perlu diketahui bahwa sinyal (secara default) melempar pengecualian, dan proses yang biasanya berjalan lama diakhiri melalui sinyal, jadi menangkap Pengecualian dan tidak berhenti pada pengecualian sinyal akan membuat program Anda sangat sulit untuk berhenti. Jadi jangan lakukan ini:
Tidak, sungguh, jangan lakukan itu. Bahkan jangan jalankan itu untuk melihat apakah itu berfungsi.
Namun, katakan Anda memiliki server berulir dan Anda ingin semua pengecualian tidak:
thread.abort_on_exception = true
).Maka ini dapat diterima di utas penanganan koneksi Anda:
Di atas berfungsi untuk variasi penangan pengecualian standar Ruby, dengan keuntungan bahwa itu tidak juga membunuh program Anda. Rails melakukan ini dalam penangan permintaannya.
Pengecualian sinyal dimunculkan di utas utama. Utas latar belakang tidak akan mendapatkannya, jadi tidak ada gunanya mencoba menangkapnya di sana.
Ini sangat berguna dalam lingkungan produksi, di mana Anda tidak ingin program Anda berhenti begitu saja ketika ada masalah. Kemudian Anda dapat mengambil tumpukan dump di log Anda dan menambahkan ke kode Anda untuk menangani pengecualian spesifik lebih lanjut di rantai panggilan dan dengan cara yang lebih anggun.
Perhatikan juga bahwa ada idiom Ruby lain yang memiliki efek yang sama:
Di baris ini, jika
do_something
memunculkan pengecualian, itu ditangkap oleh Ruby, dibuang, dana
ditugaskan"something else"
.Secara umum, jangan lakukan itu, kecuali dalam kasus khusus di mana Anda tahu Anda tidak perlu khawatir. Satu contoh:
The
debugger
Fungsi adalah cara yang agak bagus untuk mengatur breakpoint dalam kode Anda, tetapi jika berjalan di luar debugger, dan Rails, hal itu menimbulkan pengecualian. Sekarang secara teoritis Anda seharusnya tidak meninggalkan kode debug di sekitar program Anda (pff! Tidak ada yang melakukan itu!) Tetapi Anda mungkin ingin menyimpannya di sana untuk sementara waktu untuk beberapa alasan, tetapi tidak terus menjalankan debugger Anda.catatan:
Jika Anda menjalankan program orang lain yang menangkap pengecualian sinyal dan mengabaikannya, (katakan kode di atas), maka:
pgrep ruby
, ataups | grep ruby
, cari PID program Anda yang menyinggung, dan kemudian jalankankill -9 <PID>
.Jika Anda bekerja dengan program orang lain yang, untuk alasan apa pun, dibumbui dengan blok pengabaian ini, maka menempatkan ini di bagian atas garis utama adalah salah satu cara untuk keluar:
Hal ini menyebabkan program merespons sinyal terminasi normal dengan segera menghentikan, melewati penangan pengecualian, tanpa pembersihan . Sehingga bisa menyebabkan kehilangan data atau sejenisnya. Hati-hati!
Jika Anda perlu melakukan ini:
Anda benar-benar dapat melakukan ini:
Dalam kasus kedua,
critical cleanup
akan dipanggil setiap kali, apakah ada pengecualian atau tidak.sumber
kill -9
.ensure
akan berjalan terlepas dari apakah ada pengecualian yang dimunculkan atau tidak, sementararescue
itu hanya akan berjalan jika pengecualian muncul.TL; DR
Jangan
rescue Exception => e
(dan jangan naikkan kembali pengecualian) - atau Anda bisa mengusir jembatan.Katakanlah Anda berada di dalam mobil (menjalankan Ruby). Anda baru-baru ini menginstal roda kemudi baru dengan sistem peningkatan over-the-air (yang menggunakan
eval
), tetapi Anda tidak tahu salah satu programmer mengacaukan sintaks.Anda berada di jembatan, dan menyadari bahwa Anda akan sedikit ke arah pagar, jadi Anda belok kiri.
Ups! Itu mungkin Tidak Bagus ™, untungnya, Ruby memunculkan
SyntaxError
.Mobil harus segera berhenti - bukan?
Nggak.
Anda melihat sesuatu yang salah, dan Anda membanting pada istirahat darurat (
^C
:Interrupt
)Ya - itu tidak banyak membantu. Anda cukup dekat dengan rel, jadi Anda menempatkan mobil di parkir (
kill
ing:)SignalException
.Pada detik terakhir, Anda mengeluarkan kunci (
kill -9
), dan mobil berhenti, Anda membanting ke depan ke roda kemudi (airbag tidak dapat mengembang karena Anda tidak menghentikan program dengan anggun - Anda menghentikannya), dan komputer di belakang mobil Anda terbanting ke kursi di depannya. Satu kaleng penuh Coke tumpah di atas kertas. Bahan makanan di bagian belakang dihancurkan, dan sebagian besar ditutupi dengan kuning telur dan susu. Mobil itu perlu perbaikan dan pembersihan yang serius. (Data hilang)Semoga Anda memiliki asuransi (Cadangan). Oh ya - karena airbagnya tidak mengembang, Anda mungkin terluka (dipecat, dll).
Tapi tunggu! Ada
lebihalasan mengapa Anda mungkin ingin menggunakanrescue Exception => e
!Katakanlah Anda adalah mobil itu, dan Anda ingin memastikan airbag mengembang jika mobil melebihi momentum berhenti yang aman.
Ini pengecualian untuk aturan: Anda
Exception
hanya dapat menangkap jika Anda menaikkan kembali pengecualian . Jadi, aturan yang lebih baik adalah tidak pernah menelanException
, dan selalu meningkatkan kembali kesalahan.Namun menambahkan penyelamatan mudah untuk dilupakan dalam bahasa seperti Ruby, dan menempatkan pernyataan penyelamatan tepat sebelum mengangkat kembali masalah terasa sedikit tidak kering. Dan Anda tidak ingin melupakan
raise
pernyataan itu. Dan jika Anda melakukannya, semoga berhasil mencari kesalahan itu.Untungnya, Ruby luar biasa, Anda bisa menggunakan
ensure
kata kunci, yang memastikan kode berjalan. Kataensure
kunci akan menjalankan kode apa pun yang terjadi - jika ada pengecualian, jika tidak ada, satu-satunya pengecualian adalah jika dunia berakhir (atau peristiwa tidak terduga lainnya).Ledakan! Dan kode itu harus dijalankan. Satu-satunya alasan yang harus Anda gunakan
rescue Exception => e
adalah jika Anda membutuhkan akses ke pengecualian, atau jika Anda hanya ingin kode dijalankan pada pengecualian. Dan ingatlah untuk menaikkan kembali kesalahan. Setiap saat.Catatan: Seperti yang ditunjukkan @Niall, pastikan selalu berjalan. Ini bagus karena kadang-kadang program Anda bisa berbohong kepada Anda dan tidak melempar pengecualian, bahkan ketika masalah terjadi. Dengan tugas-tugas penting, seperti menggembungkan kantung udara, Anda harus memastikan itu terjadi apa pun yang terjadi. Karena itu, memeriksa setiap kali mobil berhenti, apakah ada pengecualian atau tidak, adalah ide yang bagus. Meskipun menggembungkan airbag sedikit tugas yang tidak biasa dalam kebanyakan konteks pemrograman, ini sebenarnya cukup umum dengan sebagian besar tugas pembersihan.
sumber
ensure
sebagai alternatif untukrescue Exception
menyesatkan - contohnya menyiratkan mereka setara, tetapi sebagaimana dinyatakanensure
akan terjadi apakah ada Pengecualian atau tidak, jadi sekarang kantung udara Anda akan mengembang karena Anda melebihi 5 mph meskipun tidak ada yang salah.Karena ini menangkap semua pengecualian. Tidak mungkin program Anda dapat pulih dari apa pun satunya.
Anda harus menangani hanya pengecualian yang Anda tahu cara memulihkannya. Jika Anda tidak mengantisipasi jenis pengecualian tertentu, jangan menanganinya, crash dengan keras (tulis detail ke log), lalu diagnosa log dan perbaiki kode.
Menelan pengecualian itu buruk, jangan lakukan ini.
sumber
Itu kasus spesifik aturan bahwa Anda tidak harus menangkap setiap pengecualian Anda tidak tahu bagaimana menangani. Jika Anda tidak tahu cara menanganinya, selalu lebih baik membiarkan bagian lain dari sistem menangkap dan menanganinya.
sumber
Saya baru saja membaca posting blog yang bagus tentang itu di honeybadger.io :
Pengecualian Ruby vs StandardError: Apa bedanya?
sumber