Help us understand the problem. What is going on with this article?

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)

参考

kasei-san
小林の中の人 長文はblogに。QiitaにはちょっとしたTIPSを
http://kasei-san.hatenablog.com/
lclco
全国の高速バス価格情報を比較・検索できるサービスサイトを運営
https://www.lclco.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away