Ruby Language
始動/救助による例外のキャッチ
サーチ…
基本的なエラー処理ブロック
2つの数値を分ける関数を作ってみましょう。それはその入力について非常に信頼しています:
def divide(x, y)
return x/y
end
これは多くの入力に対してうまく動作します:
> puts divide(10, 2)
5
すべてではありません
> puts divide(10, 0)
ZeroDivisionError: divided by 0
> puts divide(10, 'a')
TypeError: String can't be coerced into Fixnum
begin... end
ブロックで危険な除算操作をラップしてエラーをチェックし、 rescue
句を使用してメッセージを出力し、問題があればnil
を返すことで関数を書き直すことができます。
def divide(x, y)
begin
return x/y
rescue
puts "There was an error"
return nil
end
end
> puts divide(10, 0)
There was an error
> puts divide(10, 'a')
There was an error
エラーの保存
rescue
句でエラーを使用する場合は、エラーを保存できます
def divide(x, y)
begin
x/y
rescue => e
puts "There was a %s (%s)" % [e.class, e.message]
puts e.backtrace
end
end
> divide(10, 0)
There was a ZeroDivisionError (divided by 0)
from (irb):10:in `/'
from (irb):10
from /Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
> divide(10, 'a')
There was a TypeError (String can't be coerced into Fixnum)
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `eval'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/context.rb:380:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:489:in `block (2 levels) in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:623:in `signal_status'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:486:in `block in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `loop'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:485:in `eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:395:in `block in start'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `start'
/Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
さまざまなエラーのチェック
エラーの種類に応じて異なる処理を実行する場合は、引数として異なるエラータイプを持つ複数のrescue
句を使用します。
def divide(x, y)
begin
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
return nil
rescue TypeError
puts "Division only works on numbers!"
return nil
end
end
> divide(10, 0)
Don't divide by zero!
> divide(10, 'a')
Division only works on numbers!
rescue
ブロックで使用するためにエラーを保存する場合は、次のようにします。
rescue ZeroDivisionError => e
他のrescue
句に指定されていない型のエラーをキャッチするには、引数のないrescue
句を使用します。
def divide(x, y)
begin
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
return nil
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
end
end
> divide(nil, 2)
Don't do that (NoMethodError)
この場合、 nil
を2で除算しようとすると、 ZeroDivisionError
またはTypeError
ではないので、デフォルトのrescue
句で処理されます。このrescue
句は、 NoMethodError
あることを知らせるメッセージを出力します。
再試行
rescue
句では、 retry
を使用して、おそらくエラーの原因となった状況を変更した後にbegin
句を再度実行できます。
def divide(x, y)
begin
puts "About to divide..."
return x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
y = 1
retry
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
end
end
TypeError
を引き起こすことがわかっているパラメータを渡すと、 begin
句が実行され(ここでは「分割する」と表示してフラグを立てる)、以前と同じようにエラーがキャッチされ、 nil
が返されます。
> divide(10, 'a')
About to divide...
Division only works on numbers!
=> nil
しかし、 ZeroDivisionError
を引き起こすパラメータを渡すと、 begin
句が実行され、エラーがキャッチされ、除数が0から1に変更された後、 retry
とbegin
ブロックが(上から)異なるy
。 2回目にはエラーはなく、この関数は値を返します。
> divide(10, 0)
About to divide... # First time, 10 ÷ 0
Don't divide by zero!
About to divide... # Second time 10 ÷ 1
=> 10
エラーが発生していないかどうかの確認
エラーが発生しない場合に実行されるコードには、 else
節を使用できます。
def divide(x, y)
begin
z = x/y
rescue ZeroDivisionError
puts "Don't divide by zero!"
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
else
puts "This code will run if there is no error."
return z
end
end
else
節は、 rescue
句の1つに制御を渡すエラーがある場合は実行されません。
> divide(10,0)
Don't divide by zero!
=> nil
しかし、エラーが発生しなければ、 else
節が実行されます:
> divide(10,2)
This code will run if there is no error.
=> 5
begin
節から戻るとelse
節は実行されないことに注意してください
def divide(x, y)
begin
z = x/y
return z # Will keep the else clause from running!
rescue ZeroDivisionError
puts "Don't divide by zero!"
else
puts "This code will run if there is no error."
return z
end
end
> divide(10,2)
=> 5
常に実行する必要があるコード
常に実行したいコードがある場合は、 ensure
句を使用してください。
def divide(x, y)
begin
z = x/y
return z
rescue ZeroDivisionError
puts "Don't divide by zero!"
rescue TypeError
puts "Division only works on numbers!"
return nil
rescue => e
puts "Don't do that (%s)" % [e.class]
return nil
ensure
puts "This code ALWAYS runs."
end
end
エラーが発生した場合、 ensure
句が実行されます。
> divide(10, 0)
Don't divide by zero! # rescue clause
This code ALWAYS runs. # ensure clause
=> nil
エラーがない場合:
> divide(10, 2)
This code ALWAYS runs. # ensure clause
=> 5
確実句は、たとえばファイルが閉じられていることを確認したい場合に便利です。
else
節とは異なり、 ensure
節は begin
節またはrescue
節が値を返す前に実行される ensure
注意してください。場合はensure
句がありreturn
上書きしますreturn
、他の条項の価値を!