Hack Your Design!

Rubyで独自例外を定義するときはStandardErrorを継承する

タイトルの通り、Rubyで独自例外を定義するときはExceptionではなく、StandardError を継承するしきたりとなっています。

# `Exception`ではなく
class MyError1 < Exception; end
# `StandardError`.
class MyError2 < StandardError; end

理由をコードでみてみます。

Exception を継承した場合

class MyError1 < Exception; end

begin
  raise MyError1
rescue => e
  puts "Exception handled! #{e}"
end

# => MyError1: MyError1

MyError1 がrescue節でハンドリングされてませんね。こうしてみるとどうでしょう。

class MyError1 < Exception; end

begin
  raise MyError1
rescue Exception => e
  puts "Exception handled! #{e}"
end

# => Exception handled! MyError1

次はrescue節に入りました。

StandardError を継承した場合

class MyError2 < StandardError; end

begin
  raise MyError2
rescue => e
  puts "Exception handled! #{e}"
end

# => Exception handled! MyError2

こちらは問題なくrescue節でハンドルされました。

なぜ?

rescue は第1引数で指定した例外クラスの下の階層にある例外だけを補足するけど、引数を省略すると StandardErrorクラスを指定したものとみなすからだ。

via. Rubyで自前の例外クラスを作るときExceptionではなくStandardErrorを継承する理由

下記がビルトインのExceptionのサブクラスたちです。rescueのデフォルトがStandardErrorraiseのデフォルトがRuntimeErrorとなっています。

  • NoMemoryError
  • ScriptError
    • LoadError
    • NotImplementedError
    • SyntaxError
  • SecurityError
  • SignalException
    • Interrupt
  • StandardError – default for rescue
    • ArgumentError
      • UncaughtThrowError
    • EncodingError
    • FiberError
    • IOError
      • EOFError
    • IndexError
      • KeyError
        • StopIteration
    • LocalJumpError
    • NameError
      • NoMethodError
    • RangeError
      • FloatDomainError
    • RegexpError
    • RuntimeError – default for raise
    • SystemCallError
      • Errno::*
    • ThreadError
    • TypeError
    • ZeroDivisionError
  • SystemExit
  • SystemStackError
  • fatal – impossible to rescue

via Exception

よってrescueのデフォルトで拾えるStandardErrorを使って独自例外を定義しましょう。

class MyError2 < StandardError; end

参考

  • このエントリーをはてなブックマークに追加