:pぴー.sendせんど(:pぴー, :pぴー)

mokoaki
mokoriso@gmail.com

2017/07/22

脱出構文と例外処理、大域脱出

Ruby技術者認定試験の書籍写経メモ

脱出構文

break 繰り返しを中断して現在のループを抜ける
next 現在の回を飛ばして次の回のループに移る
redo 同じ条件でループをやり直す

例外を発生させる

# 色んな書き方ができるから逆に迷う
raise ArgumentError, 'message'
raise ArgumentError.new, 'message'
raise ArgumentError.new('message')

ArgumentError: message

デフォなら RuntimeErrorが発生する

raise 'message'
RuntimeError: message

例外処理 begin-rescue-end

begin
  p 'begin'
rescue
  p 'rescue'
else
  p 'else'
ensure
  p 'ensure'
end

"begin"
"else"
"ensure"
begin
  raise
rescue
  p 'rescue'
else
  p 'else'
ensure
  p 'ensure'
end

"rescue"
"ensure"

rescue修飾子

1 / 0 rescue 1
#=> 1

メソッド定義全体を範囲とするならbegin節を省略できるけど、しなくてもいいんじゃないかと思ってる

def moko
  1 / 0
rescue
  1
end

moko
#=> 1

例外クラスを指定した捕捉

基本的にコード処理で復帰できそうな例外がStandardErrorのサブクラスになっている

この辺りは丸暗記

Exception
  ScriptError
    SyntaxError 文法エラー
  SignalException 補足していないシグナルを受けた
  StandardError
    ArgumentError 引数の数が合わない、値が正しくない
    RuntimeError 特定の例外クラスに該当しないエラー、例外クラスを省略したraise
    NameError 未定義のローカル変数や定数を参照した
      NoMethodError 未定義のメソッドを呼び出した
    ZeroDivisionError 正数に対し、0で除算を行った

ちなみに捕捉例外クラスを指定しない場合は StandardErrorとそのサブクラスがデフォで捕捉対象となる

begin
  1 / 0
rescue NameError => e
  p e.class
rescue StandardError => e
  p e.class
end

例外オブジェクト

begin
  1 / 0
rescue ZeroDivisionError => e
  p e.backtrace
  p $!.class

  raise
end
#=> エラーオブジェクトの情報を出力した後に ZeroDivisionError を発生させる

retry

begin内でretryするともう一度最初のbeginから再実行する

hage = 0

begin
  1 / hage
rescue ZeroDivisionError
  hage = 1
  retry
ensure
  p hage
end
1
=> 1

捕捉例外を複数指定する時の注意

最初にマッチしたものしか実行されないので、バカじゃなければマッチする範囲が狭い方から指定する事

hage = 0
begin
  1 / hage
rescue
  p e.class
  raise
rescue ZeroDivisionError
  p '1つ目のrescue(StandardError)が必ず補足される為、ここは実行されることはない'
  hage = 1
  retry
end
hage = 0
begin
  1 / hage
rescue ZeroDivisionError
  p 'ZeroDivisionErrorの場合にはここが実行されるだろうし、それ以外の例外であれば下のrescueが実行される'
  hage = 1
  retry
rescue StandardError => e
  p e.class
  raise
end

catch/throwによる大域脱出

階層の深い中で処理が完了した時など、正常処理中でも処理を抜けたい時等に使うらしいが、正直使ったことがない

catch(:exit1) {
  p '実行される'
  throw :exit1
  p '実行されない!'
}
"実行される"
=> nil

呼び出し先でthrowする スタックを駆け上がる

def moko
  throw :exit2
end

catch(:exit2) {
  p '実行される'
  moko
  p '実行されない!'
}
"実行される"
=> nil

throwの第2引数にはcatch節の戻り値を指定する事もできる

def moko
  throw :exit3, '戻り値'
end

catch(:exit3) {
  p moko
  'このハゲー!'
}
=> "戻り値"

# ↑ [p moko] の p も動作していない。catch節の戻り値がirbによって表示されているだけということに注意