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以外の例外が発生したら、即座に処理を終了すべきなのである。
