自己紹介
はじめまして、はると申します。完全異業種からのエンジニア転職を目指して学習をしています。
概要
Rubyの基礎を学習しているとき、いくつか理解が難しかったところがありますが、そのうちの一つが「例外処理」でした😣
そのまま苦手だなと思いながら放置してしまっていたので、今回改めて学習しました。
注意
私は前職が完全異業種であり、英語も苦手で、スクールに入って初めてプログラミングに触れました。
そんな自分が理解しづらかった部分を、同じように初めてプログラミングの概念に触れた人に向けてまとめました。
自分の復習も兼ねて、超超噛み砕いて書いているため、周りくどい書き方になっている箇所もあるかと思います。
また、初学者のため、間違っている箇所もあるかもしれませんのでその際は教えて頂けると嬉しいです🙇
例外処理とは
『例外処理(れいがいしょり、英語: exception handling)とは、IT業界で用いられる専門用語で、ある抽象レベルにおけるシステムの設計で想定されておらず、ユーザー操作によって解決できない問題に対処するための処理である。』1)
とのことです。
なぜ例外処理が必要なのか
予想外のこと(例外)が起こった時にプログラムが異常終了してしまうと、アプリが使えなくなったり、ユーザーに不安を与えてしまうため、
例外処理をして、例外が発生した時にも何らかの形で(エラーメッセージを表示してトップページに戻すなど)プログラムの実行を継続できるようにすることが必要と考えます。
登場人物
-
raise
(fail
も全く同じ動きをしますが、今回の記事では全てraiseで説明します) begin
rescue
ensure
実は、整理すると4つだけでした!!◎
1つずつ見ていきましょう。
raise
raise
は、例外を発生させるメソッドです。
raise "例外です"
#=> Main.rb:1:in `<main>': 例外です (RuntimeError)
この1行のコードだけで、例外を発生させることができます。
"例外です"
の部分を書き換えれば、好きな例外メッセージを表示させることができます。
begin
begin
、rescue
、ensure
は例外処理のための構文です。
1つずつどのような役割か見ていきます。
begin
は、実行したいコードを書く部分です。例外が起こりうるコードの場合、begin
〜end
の例外処理の構文を使って書きます。
例えば、下記のようなメソッドがあった時、引数x
には数値が渡されることを期待していますが、
文字列が渡されるとエラーになってプログラムが終了してしまいます。(⚠️例外の発生)
def addition(x)
x = x + 1
puts x
end
addition(2) #=> 3
addition("2") #=> no implicit conversion of Integer into String (TypeError)
Rubyでは文字列と数値を足そうとするとエラーが起こります。
このような例外をキャッチするため、begin
を使ってメソッドを書き換えていきたいです。
rescue
の説明に移ります。
rescue
rescue
は、begin
とセットで使われる構文です。
begin
節の中で例外が発生したときには、例外が発生した時点でbegin
節内の処理を中断してrescue
節に飛び、
rescue
節の処理が実行されます。
def addition(x)
begin
x = x + 1
puts x
rescue => evar # everは省略してeと書くこともできます。また、どんな例外でもキャッチするようになっています。
puts "例外が発生しました:#{evar.message}" # begin内で例外が起こると直ちにrescue節に飛び、この行が実行される
end
end
addition("2")
#=> 例外が発生しました:no implicit conversion of Integer into String
これを工夫すると以下のような使い方ができます。
TypeError(型が違う)の時には、指定された型の値を入力するように促すメッセージを表示することができます。
少し実用的なイメージがついてきました!🐣
def addition(x)
begin
x = x + 1
puts x
rescue TypeError => e # TypeErrorの例外のみをキャッチするようにしました
puts "xには数値を入力してください"
end
end
addition("2")
#=> xには数値を入力してください
余談:上記のコードでは、TypeErrorを指定したことで、TypeError以外の例外はキャッチされず強制終了してしまいます。
そのため、TypeError以外の例外が起こった時にもプログラムを終了せず継続させるようにするには、以下の例のようにrescue
節を追加します。
def addition(x)
begin
x = x + 1
puts x
rescue TypeError => e # TypeErrorの例外のみをキャッチするようにしました
puts "xには数値を入力してください"
+ rescue => e
+ puts "Error: 予期せぬエラーが発生しました - #{e.message}"
end
end
Rubyでは、厳密にすべての例外をキャッチして対応するようにするには、
rescue Exception => e
と書く必要がありますが、
これでは、本来はプログラムを強制終了するべき致命的な例外の時も、プログラムを継続してしまいます。
しかし例のような書き方 rescue => e
であれば、致命的な例外を除くすべての例外(StandardError)をキャッチしてくれます。
詳しくはこちらの記事 2)で紹介されていたのでご参照ください。
言語によっても、例外処理のアプローチが異なる、ということがわかり勉強になりました💡
ensure
簡単なコードの例外処理はensure
を使わなくても実行できるため、難しい方はこの部分は読み飛ばしても大丈夫です。
ensure
は、begin
やrescue
とセットで使われる構文です。
ensure
節内に書かれたコードは、例外の有無にかかわらず、必ず実行されます。
実際に使うときの例としては、
ファイルを読み込もうとするメソッドがあった時に、ファイルが存在しないなどの例外が発生すると、ファイルを開いている処理の途中でプログラムが終了してしまいます。
ensure
は、何らかの理由で例外が起こった時でもファイルやネットワーク接続などを確実に終了するために使用されます。
先ほどまでの例のコードは、ensure
を使う必要のない簡単なコードですが、
コード例として、ensure
を無理やり追加してみると以下のようになります。
def addition(x)
begin
x = x + 1
puts x
rescue TypeError => e
puts "xには数値を入力してください"
ensure
puts "この部分は例外が発生してもしなくても必ず実行されます"
end
end
addition("2")
xには数値を入力してください
この部分は例外が発生してもしなくても必ず実行されます
raiseとbeginの構文の組み合わせの例
上記の例だと、raise
、begin
、rescue
のそれぞれの役割や動きについてはわかりましたが、
組み合わさるとよくわからなくなります😵💫
組み合わせる場合の例について考えてみます。
以下のコードでは、引数x
が数値以外の時(unless x.is_a?(Integer)
)
には、意図的にTypeErrorを引き起こさせ
rescue
節に飛ぶようになっています。
def addition(x)
begin
raise TypeError, "xには数値を入力してください" unless x.is_a?(Integer)
x = x + 1
puts x
rescue TypeError => e
puts e
end
end
addition("2")
簡単なコードなので、恩恵がわかりにくいですが、
先ほどまではrescue
節を実行するタイミングは「例外が発生した時」でしたが、
raise
を組み合わせることで、 意図したタイミングで 処理を中断しrescue
節に飛ぶことができるようになりました。
まとめ
-
raise
は、意図的に例外を発生させるメソッド。 -
begin
、rescue
、ensure
は例外処理のための構文。 -
begin
は、実行したいコードを書く部分で、begin
節内で例外が発生したときにrescue
節の処理が実行される。 -
ensure
節内に書かれたコードは、例外の有無にかかわらず、必ず実行される。 -
raise
とbegin
の構文を組み合わせることで、より意図的な動作を実現できる。
記事内ではすべてbegin
〜end
で記載しましたが、
begin
とセットじゃないrescue
も使えるとコメントで教えていただきました🙇
こちらの方がスッキリ書くことができます!
def addition(x)
x = x + 1
puts x
rescue TypeError => e
puts "xには数値を入力してください"
end
例外処理よくわからない〜が少し理解が進みました!!
ここまで読んでいただきありがとうございました🐥
参考・引用記事
引用記事
1)Wikipedia 例外処理
2)[初心者向け] RubyやRailsでリファクタリングに使えそうなイディオムとか便利メソッドとか Exceptionをrescueするのではなく、StandardErrorをrescueする