##例外処理とは
例外処理とは、エラーがでたときの処理を行うことです。
エラー原因を特定しやすくしたり、システム側で不具合が起こさないためにも、例外処理を書くことは大切です。
そこで今回は以下の使い方について簡単に紹介していきます。
・begin
・rescue
・raise
・retry
・ensure
僕も初めてみたときはただの暗号でしたが、使っていくうちにある程度理解してきました.
それではいきましょう。
##例外処理で使う、beginとrescueの使い方
例えば、システム内で10を0で割った後に、文字を出力する処理があった場合。
puts 10 / 0
puts "こんにちは"
divided by 0 (ZeroDivisionError)
このように0で割り切ろうとするとZeroDivisionError
が発生し、次の処理に移ることができません。
そこで、例外処理をいれてみましょう。
begin
10 / 0
rescue
p "0で割れません"
end
puts "こんにちは"
"0で割れません"
"こんにちは"
今度は、エラーにならずに、しっかりと最後まで処理が実行されましたね。
ここでは、
1.エラーの対象となりそうな箇所をbegin
で囲う。
2.エラーが発生した場合の処理をrescue
の中に書く。
といった書き方をしてあげることで、エラーが発生したときの対処を可能にしました。
これで万が一エラーが起きてしまった場合にも対処することができますね。
また、beginなしでワンライナーで書く時でも使えます。
hoge = 10 / 0 rescue 0
fuga = 10 / nil rescue 0
puts hoge
puts fuga
0
0
これで計算の途中でどこかに予期せぬデータが入ってエラーが発生してもrescue
の後に指定した値を返してくれます。
##resuece => e でエラー内容を格納
また、rescueの後に引数を指定してあげることで、変数の中にエラー内容を格納することができます。
begin
10 / 0
rescue => e
puts e
end
divided by 0
エラーオブジェクトが出力されました。
divided by 0
は例外クラスのオブジェクトであることも確認してみましょう。
begin
10 / 0
rescue => e
puts e.class
puts e.class.superclass
puts e.class.superclass.superclass
end
ZeroDivisionError
StandardError
Exception
ここでいうException
は、例外クラスの元となるクラスになります。(その親はObject)
##エラーごとに処理を行う
rescueの後にエラーメッセージを指定することで、
どのエラーが出た時にどの処理をするか,というのを条件分岐させることができます。
begin
10 / 0
rescue NoMethodError
puts "そのようなメソッドはありません"
rescue ZeroDivisionError
puts "0で割れません"
end
0で割れません
1.NoMethodErrorじゃない。次のrescueへ。
2.ZeroDivisionErrorだ。どんな例外処理が書いてあるんだろう。(0で割れませんを出力)
という流れで、上から順にあてはまったものから処理されていきます。
ただ注意点として、対象の例外クラスより先にその親クラスがあった場合、その親クラスが先に実行されてしまうということ。
実際にみてみましょう。
begin
10 / 0
rescue StandardError
puts "親クラスは先に実行されるよ"
rescue ZeroDivisionError
puts "0で割れません"
end
親クラスは先に実行されるよ
ZeroDivisionError
の親クラスはStandardError
なので、そっちが先に実行されてしまいました。
これではエラーの原因が分からなくなってしまうので、一番起こりそうなエラーから書くようにしましょう。
##例外処理にでてくる、raiseの使い方
raiseで任意の例外を発生させることができます。
begin
raise NoMethodError
rescue => e
p e
end
NoMethodError
使用用途としては、
・パラメータが想定されたものではない
・不正なアクセスがきた
といった場合に, 明示的にエラーを発生させ、処理を中断させたいときに使います。
また、独自のエラーを作ることも可能で、そのときはStandardError
を継承させてあげます。
class Hoge < StandardError #例外クラスを継承
end
begin
raise Hoge
rescue => e
p e
end
#<Hoge: Hoge>
##retryの使い方
retryを使うことで、エラーが起きても、再度beginに戻って実行することができます。
num = 0
begin
puts 10 / num
rescue ZeroDivisionError => e
puts e
num = 1
retry #beginブロックを再度実行
end
puts "終了しました"
divided by 0
10
終了しました
処理の流れとしては,
- 変数numが0なので、
10 / 0
でZeroDivisionError
が発生 - **例外オブジェクトを出力。**変数numの0が1に代わり, retryでbeginを再度実行
- 変数numが1なので、
10 / 1
で 10を出力 - 例外エラーはないのでbegin内の実行は終了し、最後に終了しましたを出力
また、注意点としては、再度実行してもエラーが解決されない場合は永遠に無限ループしてしまうこと。
例えば、先ほどのコードでnumに1を足さない場合を想定してみましょう。
num = 0
begin
puts 10 / num
rescue ZeroDivisionError => e
puts e
num = 0 #0を足す
retry
end
puts "終了しました"
divided by 0
divided by 0
divided by 0
....
このように、永久に無限ループしてしまい、誤った使い方をするとサーバーがクラッシュするので気をつけましょう。
強制終了はCtrl + C
です。
##ensureの使い方
ensureを使うことで、例外が発生してもしなくても実行される処理を書くことができます。
begin
puts "例外なし"
rescue => e
puts e
ensure
puts "ensureは絶対実行する!"
end
例外なし
ensureは絶対実行する!
使用用途としては、何らかの処理を行っている最中にエラーが発生した場合でも、確実に実行したい処理がある場合に使い書います。
(例: ファイルを開いている最中に不具合が発生しても確実に閉じるようにする)
##まとめ
1. begin・・・ 例外処理を始めるときに一番最初に書くもの
2. rescue ・・・ 例外処理の内容を書く場所
3. retry ・・・ エラーが起きても、beginに戻って例外処理を再度実行するもの
4. raise ・・・ 任意のエラーを明示的に発生させる
5. ensure ・・・ 例外が発生してもしなくても確実に実行される処理を書く