RyanDavisのRubyQuickRefは(説明なしで)次のように述べています。
例外を救出しないでください。これまで。または私はあなたを刺します。
なぜだめですか?正しいことは何ですか?
TL;DR :StandardError
一般的な例外キャッチの代わりに使用します。元の例外が再発生した場合 (例: 例外のみをログに記録するためにレスキューする場合)、レスキューException
はおそらく問題ありません。
Exception
はRuby の例外階層のルートなので、、 、 などのサブクラスを含むすべてrescue Exception
からレスキューすると、.SyntaxError
LoadError
Interrupt
RescueInterrupt
により、ユーザーは を使用CTRLCしてプログラムを終了できなくなります。
レスキューSignalException
を行うと、プログラムがシグナルに正しく応答できなくなります。以外では殺せませんkill -9
。
レスキューSyntaxError
とはeval
、失敗した s が黙ってそうするということです。
これらはすべて、このプログラムを実行して表示することができCTRLCますkill
。
loop do
begin
sleep 1
eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts "I refuse to fail or be stopped!"
end
end
レスキューException
はデフォルトではありません。やっている
begin
# iceberg!
rescue
# lifeboats
end
から救出するのではなくException
、 から救出しStandardError
ます。通常は、デフォルトよりも具体的なものを指定する必要がありますStandardError
が、それを回避するとスコープが狭くなるのではなくException
広がり、破滅的な結果を招き、バグハンティングが非常に困難になる可能性があります。
レスキューしたい状況がStandardError
あり、例外のある変数が必要な場合は、次の形式を使用できます。
begin
# iceberg!
rescue => e
# lifeboats
end
これは次と同等です:
begin
# iceberg!
rescue StandardError => e
# lifeboats
end
レスキューするのが正気な数少ない一般的なケースの 1 つはException
、ログ記録/レポートの目的です。この場合、すぐに例外を再発生させる必要があります。
begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end
本当のルールは次のとおりです。例外を捨てないでください。あなたの引用の著者の客観性は、それがで終わるという事実によって証明されるように、疑わしいです.
または私はあなたを刺します
もちろん、シグナルは (デフォルトで) 例外をスローし、通常は長時間実行されるプロセスはシグナルによって終了されることに注意してください。そのため、例外をキャッチしてシグナル例外で終了しないと、プログラムを停止するのが非常に困難になります。だからこれをしないでください:
#! /usr/bin/ruby
while true do
begin
line = STDIN.gets
# heavy processing
rescue Exception => e
puts "caught exception #{e}! ohnoes!"
end
end
いいえ、本当に、しないでください。それが機能するかどうかを確認するためにそれを実行しないでください。
ただし、スレッド化されたサーバーがあり、すべての例外を次のようにしたくないとします。
thread.abort_on_exception = true
)。 次に、これは接続処理スレッドで完全に受け入れられます。
begin
# do stuff
rescue Exception => e
myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
myLogger.error("Stack trace: #{backtrace.map {|l| " #{l}\n"}.join}")
end
上記は、Ruby のデフォルトの例外ハンドラーの変形であり、プログラムを強制終了しないという利点があります。Rails はリクエスト ハンドラでこれを行います。
メインスレッドでシグナル例外が発生します。バックグラウンド スレッドはそれらを取得しないため、そこでキャッチしようとしても意味がありません。
これは、何か問題が発生したときにプログラムを単純に停止させたくない本番環境で特に役立ちます。次に、ログ内のスタック ダンプを取得し、コードに追加して、コール チェーンのさらに下にある特定の例外をより適切な方法で処理できます。
また、ほぼ同じ効果を持つ別の Ruby イディオムがあることにも注意してください。
a = do_something rescue "something else"
この行でdo_something
は、例外が発生した場合、Ruby によってキャッチされ、破棄され、a
割り当てられ"something else"
ます。
心配する必要がないとわかっている特別な場合を除いて、通常はそうしないでください。一例:
debugger rescue nil
このdebugger
関数は、コードにブレークポイントを設定するためのかなり優れた方法ですが、デバッガーや Rails の外部で実行すると、例外が発生します。理論的には、デバッグ コードをプログラム内に置いたままにしておくべきではありません (pff! 誰もそんなことはしません!) が、何らかの理由でしばらくそこに置いておきたいと思うかもしれませんが、デバッガーを継続的に実行したくはありません。
ノート:
シグナル例外をキャッチしてそれらを無視する他の誰かのプログラムを実行した場合 (上記のコードを言ってください)、次のようになります。
pgrep ruby
、またはと入力ps | grep ruby
し、問題のあるプログラムの PID を探してから を実行しますkill -9 <PID>
。 なんらかの理由でこれらの無視例外ブロックが散りばめられている他の誰かのプログラムを使用している場合、これをメインラインの一番上に置くことは、1 つの考えられる対処法です。
%W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
これにより、プログラム はクリーンアップなしで例外ハンドラをバイパスして即座に終了することで、通常の終了シグナルに応答します。そのため、データの損失などを引き起こす可能性があります。気をつけて!
これを行う必要がある場合:
begin
do_something
rescue Exception => e
critical_cleanup
raise
end
あなたは実際にこれを行うことができます:
begin
do_something
ensure
critical_cleanup
end
2 番目のケースでcritical cleanup
は、例外がスローされるかどうかにかかわらず、毎回呼び出されます。
これはすべての例外をキャプチャするためです。プログラムがそれらのいずれからも回復できる可能性はほとんどありません。
回復方法がわかっている例外のみを処理する必要があります。特定の種類の例外が予想されない場合は、それを処理せず、大声でクラッシュさせ (ログに詳細を書き込みます)、ログを診断してコードを修正します。
例外を飲み込むのは悪いことです。そうしないでください。
これは、処理方法がわからない例外をキャッチしてはならないという規則の特定のケースです。処理方法がわからない場合は、システムの他の部分にキャッチして処理させることをお勧めします。