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

  • 215
    いいね
  • 3
    コメント
この記事は最終更新日から1年以上が経過しています。

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

先に結論

  • 型指定なしで 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)

参考