292
235

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 5 years have passed since last update.

【Rails】例外処理の書き方(begin, rescue, raise,retry, ensure)

Last updated at Posted at 2018-11-10

##例外処理とは
例外処理とは、エラーがでたときの処理を行うことです。
エラー原因を特定しやすくしたり、システム側で不具合が起こさないためにも、例外処理を書くことは大切です。

そこで今回は以下の使い方について簡単に紹介していきます。
・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
終了しました

処理の流れとしては,

  1. 変数numが0なので、10 / 0ZeroDivisionErrorが発生
  2. **例外オブジェクトを出力。**変数numの0が1に代わり, retryでbeginを再度実行
  3. 変数numが1なので、10 / 110を出力
  4. 例外エラーはないので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 ・・・ 例外が発生してもしなくても確実に実行される処理を書く

292
235
2

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
292
235

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?