9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ruby 例外について

Last updated at Posted at 2018-08-12

例外とは

プログラム実行時に発生したエラー(予想外の動作)のこと

例えば以下のような動作のことをいう

  • 存在しないメソッドが呼び出された
  • 存在しないカラムにアクセスした
  • APIでリクエストを投げたがレスポンスが返ってこない

例外が発生した際は、そのままプログラムを走らせると予期しない結果になる可能性があるため、例外に応じてリカバリ処理を入れたり処理をスキップさせるなどの対応が必要になる(例外処理)。

例外をつかまえる(例外を処理する)

  • begin〜rescue〜end
構文
begin

  #例外を発生させる可能性のある処理

rescue [捕まえたい例外]

  #例外が起こった場合の処理

end

rescueの第1引数には捕まえたい例外クラスを指定

第1引数で指定した例外クラスの下の階層にある例外だけを捕捉する。
デフォルト(引数省略時)は、StandardErrorクラスを指定したとみなす。

例外を発生させる

  • raiseメソッド
    Kernelモジュールで定義されているメソッド。
raiseを実行
# 引数に文字列を指定した場合
#    ==> 文字列をメッセージとする`RuntimeError`例外を発生させる
raise "エラー発生"
RuntimeError: エラー発生

#引数に例外クラスを指定した場合
#    指定した例外クラスの例外を発生させる
raise ArgumentError
ArgumentError: ArgumentError

独自の例外クラスをつくる

独自の例外クラスとは?
Rubyで用意されている標準例外クラスとは別で独自の例外クラスを使いたい時に、独自例外クラスを定義して任意の例外に対して独自の例外を発生できるようにする。
独自の例外クラスを作成したいときは、
独自の例外クラスをつくるには、StandardErrorを継承する(推奨)。


・独自例外クラスを定義

XXXApiTimeException
class XXXApiTimeException < StandardError
end

・独自例外を発生させ、捕捉する

begin〜rescue〜end
begin
  xxxxxx #APIがタイムアウトするような例外が発生する処理
  raise XXXApiTimeException

rescue XXXApiTimeException
  xxxxxx #リカバリ処理

end

継承にStandardErrorクラスが推奨される理由

例外クラスを実装する場合には、標準の例外クラスのどれかを継承すれば良いとあるのになぜStandardErrorの継承が推奨されるのか?

そもそも標準例外クラスとは?
階層は以下の通り。

標準例外クラスの階層(一部を抜粋)
Exception #最上位
  |--NoMemoryError
  |--ScriptError
  |    |--LoadError
  |    |--SyntaxError
  |
  |--StandardError #独自例外クラスを生成する際に継承を推奨されているクラス
  |   |--ArgumentError
  |   |--NameError
  |   |    |--NoMethodError
  |   |
  |   |
  |   |--RuntimeError
  |   |--TypeError
  |   |--ZeroDivisionError
  |
  |--SystemExit
  |--SystemStackError
  • Exceptionクラスを継承して独自例外クラスをつくるとどうなるのか
    上記の例外クラスの階層を踏まえて、例えばStandardErrorクラスよりも上位となるExceptionクラスを独自例外クラスの継承クラスとする場合を考えてみる
Exceptionクラスを継承した場合_例1
class TestError < Exception #独自例外クラス
end

begin
  p "hello world"
  raise TestError

rescue Exception => error
  p "例外をキャッチしたよ"
  p error

end

# 結果
"hello world"
"例外をキャッチしたよ"
#<TestError: TestError>
Exceptionクラスを継承した場合_例2
class TestError < Exception #独自例外クラス
end

begin
  p "hello world"
  test = 1/0 #0で除算するとZeroDivisionErrorが発生
  raise TestError

rescue Exception => error
  p "例外をキャッチしたよ"
  p error

end

#結果
"hello world"
"例外をキャッチしたよ"
#<ZeroDivisionError: divided by 0>

上記の例1では、TestErrorクラスの例外を捕捉、例2ではZeroDivisionErrorを捕捉している。ここで問題なのは、Exceptionクラスをrescueで捕捉する場合、捕捉する対象が全ての例外クラスとなるのでrescueでのリカバリ処理に何を書けばいいか特定できない点にある。

以上からExceptionクラスを継承した独自例外クラスを作成した場合は、例外処理がコントロールしにくいことがわかる。結果として、その下位のStandardErrorクラスの継承が現実的。またrescueの引数を省略した場合も捕捉するクラスはStandardErrorクラスとなっていることもStandardErrorクラスの継承を推奨する理由の1つとしても良いと思う。

StandardErrorクラスを継承した独自例外クラスをつくろう
class TestError < StandardError #独自例外クラス
end
9
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?