LoginSignup
3
2

More than 5 years have passed since last update.

[Ruby入門] 13. 例外処理

Last updated at Posted at 2017-04-24

>> 連載の目次は こちら!

■ Rubyの例外クラスの構成


Exception
    |
    |- StandardError
    |   |- TypeError とか RuntimeError とかの、アプリケーション側に起因する例外たち
    |   |- 僕らがアプリケーション向けに独自に作成する例外たち
    |
    |- その他の例外: メモリのエラーみたいな、システム側の例外。アプリケーション側では救えない類。

  • 例外の基底クラスは Exception クラス
  • その配下は、大きく StandardErrorクラス系と、それ以外に分かれる
  • StandardErrorクラス系は、いわゆるアプリケーション側の例外
  • それ以外はシステム側のエラーで、通常はアプリケーションでは復帰できない例外たち(メモリのエラーとか)
  • 通常の例外クラスは、XxxError という命名が正しいのかな。XxxExceptionじゃなくて。

・rescueでキャッチできるのは StandardError系だけ
↑間違いです! 型を明示してrescueすれば、それ以外の例外もちゃんと拾えます。
コメント欄の @scivola さんの記述も参照してください。

■ begin〜rescue〜end (いわゆる try〜catch 的な)

● 一番シンプルに試してみる

begin
  raise "oh my god !!"  # (1)
rescue => e             # (2)
  p e   # (3)  #<RuntimeError: oh my god !!>
  p $!  # (4)  #<RuntimeError: oh my god !!>
end

(1) raise で例外を投げる(throw的な)。例外クラスを指定しない場合、RuntimeError が投げられる
(2) rescue で catch する。例外の型を指定しない場合、StandardError系の全ての例外をキャッチする
(3) rescue が受け取った e が、例外オブジェクト。
(4) グローバル変数「$!」にも、例外オブジェクトがセットされている

● 型を指定してrescueしてみる

def exception_test(num)

    begin

        case num
        when 1
            # raiseする
            raise ArgumentError.new, "bad argument"
        when 2
            # 0で割っちゃう
            5 / 0
        when 3
            # 無いメソッド呼んじゃう
            String.not_exist_method
        else
            # もうどうしようもないやつ
            raise RuntimeError, "oh my god!"
        end

    # 例外の型を明示してresucue
    rescue ArgumentError => e
        print "rescue 1: "; p e

    # 例外の型は複数指定してresucueできる
    rescue ZeroDivisionError, NoMethodError => e
        print "rescue 2: "; p e

    # 例外の型を指定しなければ、StandardError系すべてresucueできる
    # 以下は「rescue StandardError => e」って書くのと同じこと。
    rescue => e
        print "rescue 3: "; p e
    end

end

exception_test(1)
   # rescue 1: #<ArgumentError: bad argument>

exception_test(2)
   # rescue 2: #<ZeroDivisionError: divided by 0>

exception_test(3)
   # rescue 2: #<NoMethodError: undefined method `not_exist_method' for String:Class>

exception_test(4)
   # rescue 3: #<RuntimeError: oh my god!>

■ 独自の例外クラスを定義する

# StandardError を継承する。もちろんここからさらにサブクラス階層化しても良し。
class OverWorkError < StandardError
  attr_reader :hour

  # 例外のnewで引数を受け取ってインスタンス変数で保持してみる
  def initialize(hour)
    @hour = hour
  end
end

begin
  (1..10).each do |i|
      raise OverWorkError.new(i), "働きすぎ!" if (i > 8)
      print i.to_s + ".."
  end

rescue OverWorkError => e
  # 例外オブジェクトからインスタンス変数を取得。messageメソッドでメッセージを取得
  puts e.hour.to_s + "時間なんて" + e.message

end

# 実行結果
# 1..2..3..4..5..6..7..8..9時間なんて働きすぎ!

■ 例外が発生しなかった場合になんかする(else)

begin
  print "予想外のことが全く起きないって..."
rescue => e
  p e
else
  puts "けっこう退屈かもねー"
end

■ 例外が発生しようがしまいがなんかする(ensure)

begin
  print "世は全てこともなし..."
rescue => e
  p e
ensure
  puts "で、でもなんか言わせてー!"
end

■ 例外発生時にリトライする(retry)

i = 0
begin
  i += 1
  raise OverWorkError.new(i), "働きすぎ!"
rescue => e
  # 無限ループに注意!なんか条件付きでretryすること。
  retry if (i <= 8)
  puts e.hour.to_s + "時間なんて" + e.message  # 9時間なんて働きすぎ!
end

# 実行結果
# 9時間なんて働きすぎ!

■ 簡易なrescue(後置rescue)

  raise OverWorkError.new(i), "働きすぎ!" rescue puts "おうちに帰ろう!"
  # この場合、StandardError系をまとめてrescueする
3
2
4

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
3
2