Edited at

rubyの例外についてまとめてみた

More than 3 years have passed since last update.

ちょっとこんがらがったので整理


先に結論


  • 型指定なしで rescue すると、 StandardError のサブクラスのみ catch する

  • アプリケーションレベルでの標準的なエラーは RuntimeError のサブクラス



    • RuntimeError は、StandardError のサブクラス



  • なので、アプリケーション内で独自の例外を書く場合、普通は RuntimeError を使ったほうが良い

  • 1.8 系の場合 Timeout::Error は、StandardError のサブクラスではない


raise 関数に型を渡さない時に発生する例外は、 RuntimeError

raise "エラーメッセージ" # => RuntimeError が発生


RuntimeError って?


特定の例外クラスには該当しないエラーが起こったときに発生します。

また Kernel.#raise で例外クラスを指定しなかった場合も RuntimeError が発生します。


class RuntimeError (Ruby 2.3.0)


rescue でクラスを省略した時にcatchする例外は、 StandardError

rescue => e # => StandardError を catch

end


通常のプログラムで発生する可能性の高い 例外クラスを束ねるためのクラスです。

StandardError とそのサブクラスは、 rescue 節でクラスを省略したときにも捕捉できます。


class StandardError (Ruby 2.3.0)


Rubyの組み込み例外は(SystemExit や Interrupt のような脱出を目的としたものを除いて) StandardError のサブクラスです。


制御構造 (Ruby 2.3.0)


Exception の位置づけは?


全ての例外の祖先のクラスです。


class Exception (Ruby 2.3.0)


StandardError は、 Exception のサブクラス



  • Exception は、システム関係の例外も含む

  • アプリケーションレベルの例外であれば、 StandardError を使うべきという思想らしい

  • そのため、 rescue で、型指定をしない場合は、 StandardError を継承している例外のみ catch する

begin

raise "エラーメッセージ" # catch する
raise Exception.new("エラーメッセージ") # catch しない
rescue => e
p e.message
end


RuntimeErrorStandardError のサブクラス



  • StandardError は、通常のプログラムで発生すエラーを意味するクラス


    • これを継承した場合は、通常のプログラムで発生すエラーを意味することになる




  • RuntimeError は、StandardError の中で、特定の例外クラスには該当しないエラーを意味するクラス


    • 「クラス指定のない raise 」専用のクラスといえる




Exceptionrescue するとどんな時困るの?

例えばこんな時

1.upto(10) do |i|

begin
p i
sleep(1)
rescue Exception => e
p e.class
p "catch!"
end
end

これを実行中に、ctrl-c で処理を停止しようとすると、 Interrupt 例外が発生して、処理が続行されちゃう

けど、それって意図したことじゃないよね

$ ruby 01.rb

1
2
^CInterrupt
"catch!"
3
4
5
^CInterrupt
"catch!"
6
7
8
9
10


1.8 系での Timeout::Error の罠

1.8 系だとTimeout::Error が、StandardError ではなく、Interrupt を継承しているため、

型指定なしでの rescue では、catch されないという罠がある

Class: Timeout::Error (Ruby 1.8.7)

例えば、こんな時に、タイムアウトが発生しても catch されない

require 'net/http'

begin
Net::HTTP.start("test.net") do |http|
...
end
rescue =>e
...
end

例外の型指定は複数の型を指定できるので、こんな回避方法もある

begin

....
rescue Timeout::Error, StandardError =>e
end

ちなみに 1.9 系以降では、StandardError になっているので、解決している

Class: Timeout::Error (Ruby 1.9.3)


参考