Rubyの例外処理の挙動と例外の階層について
タグ: ruby / 初版公開: 2014-02-11

Rubyの例外処理について基本と注意点をまとめた。

例外処理の文法

Rubyで例外処理を行うのはbegin-rescue-else-ensure-end構文である。JavaやC++でいうtry-catchと使い方は概ね同じである。以下にリファレンスマニュアから引用して文法を示す。

begin
  式..
[rescue [error_type,..] [=> evar] [then]
  式..]..
[else
  式..]
[ensure
  式..]
end

begin節の処理で例外が発生した場合はrescue節が評価され、例外が発生しなかった場合はelse節が評価される。例外の発生有無に関わらず、ensure節はbegin節の実行後に必ず評価される。

例外処理の注意点

rescue節はerror_typeを指定することで、発生する例外と対応させて複数記述することができる。error_typeを省略した時は、ExceptionではなくStandardErrorを指定したのと同じになることに注意しよう。以下の2つは同一である。

begin
  # do something
rescue 
  # recover
end
begin
  # do something
rescue StandardError
  # recover
end

error_typeに明示的にExceptionを指定することもできるが、普通はExceptionを捕捉するようにしてはいけない。この説明は例外の階層で後述する。

よりエレガントな例外処理

例外処理を簡潔に記述するためにrescue修飾子がある。この修飾子は以下の文法で、式1で例外が発生したときに、式2を評価する。

式1 rescue 式2

rescue修飾子は以下のようなbegin-rescue-endと同等であり、省略形と言える。

begin
  式1
rescue
  式2
end

もう1点、リファレンスマニュアルには以下の記載がある。さらっと書いてあるが重要なことだ。 メソッド定義の中ではbegin-endを省略することができるというのだ。

クラス/メソッドの定義/クラス定義、クラス/メソッドの定義/モジュール定義、クラス/メソッドの定義/メソッド定義 などの定義文では、それぞれ begin なしで rescue, ensure 節を定義でき、これにより例外を処理することが できます。

つまり以下のような例外処理は、

def foo
  begin
    # do someting
  rescue
    # recover
  end
end

冗長なbegin-endを省略して、次のように書けるというわけだ。

def foo
  # do someting
  rescue
    # recover
end

例外の階層

以下にRuby 1.9.3の組み込み例外クラスの一覧を示す。(抽出したのはExceptionの子クラスと孫クラスまで)

  • Exception
    • fatal
    • NoMemoryError
    • ScriptError
      • NotImplementedError
      • LoadError
      • SyntaxError
    • SecurityError
    • SignalException
      • Interrupt
    • StandardError
      • ArgumentError
      • EncodingError
      • FiberError
      • IOError
      • IndexError
      • LocalJumpError
      • Math::DomainError
      • NameError
      • RangeError
      • RegexpError
      • RuntimeError
      • SystemCallError
      • ThreadError
      • TypeError
      • ZeroDivisionError
    • SystemExit
    • SystemStackError

一般的な例外はすべてStandardErrorのサブクラスとなっていることがわかる。

またStandardError以外のExceptionのサブクラスには、メモリ不足で発生するNoMemoryErrorや、シグナルを受信した際に発生するSignalException(Interrupt)など、即座にスクリプトを中止すべき例外が並んでいることもわかる。

この例外の階層こそ、注意点で述べたようにRubyがrescueで捕捉するデフォルトの例外がStandardErrorであり、安易にExceptionを捕捉してはならない理由である。

基本的には、StandardErro以外の例外が発生したら、即座に処理を終了すべきなのである。

参考