ちょっとこんがらがったので整理
先に結論
- 型指定なしで 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 のサブクラスです。
Exception の位置づけは?
全ての例外の祖先のクラスです。
StandardError は、 Exception のサブクラス
-
Exceptionは、システム関係の例外も含む - アプリケーションレベルの例外であれば、
StandardErrorを使うべきという思想らしい - そのため、
rescueで、型指定をしない場合は、StandardErrorを継承している例外のみcatchする
begin
raise "エラーメッセージ" # catch する
raise Exception.new("エラーメッセージ") # catch しない
rescue => e
p e.message
end
RuntimeError は StandardError のサブクラス
-
StandardErrorは、通常のプログラムで発生すエラーを意味するクラス- これを継承した場合は、通常のプログラムで発生すエラーを意味することになる
-
RuntimeErrorは、StandardErrorの中で、特定の例外クラスには該当しないエラーを意味するクラス- 「クラス指定のない
raise」専用のクラスといえる
- 「クラス指定のない
Exception を rescue するとどんな時困るの?
例えばこんな時
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)