950

RyanDavisのRubyQuickRefは(説明なしで)次のように述べています。

例外を救出しないでください。これまで。または私はあなたを刺します。

なぜだめですか?正しいことは何ですか?

4

6 に答える 6

1429

TL;DR :StandardError一般的な例外キャッチの代わりに使用します。元の例外が再発生した場合 (例: 例外のみをログに記録するためにレスキューする場合)、レスキューExceptionはおそらく問題ありません。


ExceptionRuby の例外階層のルートなので、、 、 などのサブクラスを含むすべてrescue Exceptionからレスキューすると、.SyntaxErrorLoadErrorInterrupt

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
于 2012-04-06T19:38:01.770 に答える
85

本当のルールは次のとおりです。例外を捨てないでください。あなたの引用の著者の客観性は、それがで終わるという事実によって証明されるように、疑わしいです.

または私はあなたを刺します

もちろん、シグナルは (デフォルトで) 例外をスローし、通常は長時間実行されるプロセスはシグナルによって終了されることに注意してください。そのため、例外をキャッチしてシグナル例外で終了しないと、プログラムを停止するのが非常に困難になります。だからこれをしないでください:

#! /usr/bin/ruby

while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
end

いいえ、本当に、しないでください。それが機能するかどうかを確認するためにそれを実行しないでください。

ただし、スレッド化されたサーバーがあり、すべての例外を次のようにしたくないとします。

  1. 無視する (デフォルト)
  2. サーバーを停止します (これは、 と言った場合に発生します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! 誰もそんなことはしません!) が、何らかの理由でしばらくそこに置いておきたいと思うかもしれませんが、デバッガーを継続的に実行したくはありません。

ノート:

  1. シグナル例外をキャッチしてそれらを無視する他の誰かのプログラムを実行した場合 (上記のコードを言ってください)、次のようになります。

    • Linux では、シェルでpgrep ruby、またはと入力ps | grep rubyし、問題のあるプログラムの PID を探してから を実行しますkill -9 <PID>
    • Windows では、タスク マネージャー ( CTRL- SHIFT- ESC) を使用し、[プロセス] タブに移動してプロセスを見つけ、右クリックして [プロセスの終了] を選択します。
  2. なんらかの理由でこれらの無視例外ブロックが散りばめられている他の誰かのプログラムを使用している場合、これをメインラインの一番上に置くことは、1 つの考えられる対処法です。

    %W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
    

    これにより、プログラム はクリーンアップなしで例外ハンドラをバイパスして即座に終了することで、通常の終了シグナルに応答します。そのため、データの損失などを引き起こす可能性があります。気をつけて!

  3. これを行う必要がある場合:

    begin
      do_something
    rescue Exception => e
      critical_cleanup
      raise
    end
    

    あなたは実際にこれを行うことができます:

    begin
      do_something
    ensure
      critical_cleanup
    end
    

    2 番目のケースでcritical cleanupは、例外がスローされるかどうかにかかわらず、毎回呼び出されます。

于 2012-04-07T05:30:41.647 に答える
48

これはすべての例外をキャプチャするためです。プログラムがそれらのいずれからも回復できる可能性はほとんどありません。

回復方法がわかっている例外のみを処理する必要があります。特定の種類の例外が予想されない場合は、それを処理せず、大声でクラッシュさせ (ログに詳細を書き込みます)、ログを診断してコードを修正します。

例外を飲み込むのは悪いことです。そうしないでください。

于 2012-04-06T19:21:19.510 に答える
12

これは、処理方法がわからない例外をキャッチしてはならないという規則の特定のケースです。処理方法がわからない場合は、システムの他の部分にキャッチして処理させることをお勧めします。

于 2012-04-06T23:54:05.867 に答える