
プログラミング学習から転職成功まで導く、当社人気のDMM WEBCAMP(旧WEBCAMP PRO)。
2月生は満員となっております。3月生募集に向け、お早めの申込みをオススメします。
・プログラミング未経験でもエンジニア転職を絶対成功させたい
・スキルを身に着けて人生を自ら切り開きたい
上記にあてはまる方は、ぜひご検討ください!
▼目次
はじめに
Rubyのプログラムを作っていると、様々なエラーが発生します。
変数やメソッドの名前や使い方の間違いは分かりやすいですし、修正しなければいけません。
しかし、実行中に発生するエラーについては、エラーが発生する原因が様々で、プログラムそのものを変更してエラーが発生しないようにする、つまりエラーを考慮してプログラムを作ることが必要です。
その修正方法としては、エラーになるような状況が発生しないようにするのがベストですが、ネットワークへのアクセスやユーザーが入力する情報などは、「エラーが発生することを前提とした」対応を行わなければいけません。
そういった場合に有効なのが、例外処理です。
ここでは、rescueをはじめとした例外処理に必要な式と、その使い方について詳しく紹介します。
rescueで例外処理をする
「例外処理」というのは、プログラムの実行中に発生するエラーに対応した処理のことです。
そんな例外処理に、rescueをはじめとしたいくつかの式が使われるのです。
ここでは、まず「例外処理」について詳しく説明しましょう。
例外処理とは?
「例外」というのは、プログラム実行中に発生するエラーのことです。
ただし、例外には以下のようにいくつかの種類があります。
1.記載ミスによる例外(メソッド名の書き間違いなど)
2.使い方のミスによる例外(文字列と整数を加算するなど)
3.考慮漏れによる例外(想定外の戻り値など)
4.外部的な例外(システムからの応答がない、ファイルがないなど)
このうち、1.と2.については、プログラムコードを修正して対処しなければいけません。(この2点をスムーズに修正できるために、本稿のような記事があると言ってもよいでしょう)
3.についても、きちんと考慮してプログラムコードで処理をして、例外が発生しないようにするのが基本的な対処方法と言えます。
しかし、4.については、外部の問題であるため、この例外を、「プログラムコードを修正して」防ぐ手段がありません。また、3.についても、考慮すべき対象が広すぎる場合などは、どこまで対応すべきか、工数や予算などとの相談が必要になってくるでしょう。
だからといって、そのままにしていると例外が発生した瞬間にプログラムが中断し、正しい終了処理を行わずにシステムにダメージを与えてしまうようなリスクがある場合もあります。
そのため、「例外が発生することを前提として、例外が発生しても処理を継続できる、もしくは被害を最低限にして正常にプログラムを終了する」ための処理を実装しておく必要があるわけです。
それが、「例外処理」なのです。
例外オブジェクトException
例外が発生すると、Rubyはただ処理を中断するのではなく、例外の内容などの情報を収めた例外オブジェクト(Exception)を生成します。
Exceptionの中には、例外の原因を示すIDや、例外が発生したプログラムコード上の場所、メソッド呼び出しのスタック(メソッドがどの順番で呼ばれているか)などの情報がつまっており、プログラムコードを修正するためのヒントを与えてくれるようになっています。
これまで、サンプルプログラムなどを作成していく中で、エラーが発生した経験のある人は、表示されたエラーメッセージをヒントに修正箇所を見つけた人もいると思います。
コマンドプロンプト上に表示されたあの情報は、Exceptionオブジェクトに格納されている情報が表示されていたわけです。
例外処理をするための式(制御構造)
例外処理で使用する式は、以下の通りです。
式・メソッド | 説明 |
---|---|
rescue | 例外が発生した場合のみ行う処理を定義する キャッチする例外の種類を指定できる |
ensure | 例外が発生してもしなくても、必ず行う処理を定義する |
retry | beginからやり直す rescue節の中でのみ使用可能 |
raise | わざと例外を発生させる |
特に、例外処理をプログラムコードに記載するためには、「rescue」は必須です。それ以外については、対処する方法によります。
例外処理(rescue、ensure)
では、前述した例外処理のための式やメソッドについて、詳しく解説していきましょう。
例外を補足する(rescue)
例外処理で中心となるのが、rescueです。
まずは構文を紹介しましょう。
begin 処理A rescue [例外タイプ][=> 変数] 処理B(rescue節) [else 処理C] end[例外タイプ]を省略した場合は、すべての例外に対して同じ処理を行います。 [=> 変数]は省略可能です。記載した場合は、変数にExceptionオブジェクトが代入されます。(省略してもしなくても、特殊変数$!には、Exceptionオブジェクトが代入されています)
また、[else 処理C]も省略可能です。
処理Aの中で例外が発生しなければ、rescue節は無視され、elseがあれば処理Cが実行されます(elseがなければ、end以降の処理へ遷移します)。しかし、例外が発生した場合は、その瞬間に処理がrescue節へ遷移して、rescue節が実行されます。
※この「処理がrescue節へ遷移すること」を「キャッチする」と言うことがあります。
そのため、例外が発生した瞬間に処理が終了することがありません。より詳しい例外の内容を表示したり、処理を継続できるようにデータを修正したりといった対応ができ、プログラムを継続させることもできるのです。
例えば、ユーザーがデータを入力するなど、与えられるデータの整合性を完全にできないような場合や、外部から提供される情報で不正なデータを事前に除去することができない場合などに応用できるでしょう。
rescue00-1.rb
peoples = [5,1,8,0,3] begin price = 1000 p("#{price}を割り勘すると...") peoples.each do |num| p(price / num) end rescue => ex p(ex) ensure p("終了") end
(結果)
"1000を割り勘すると..." 200 1000 125 #<ZeroDivisionError: divided by 0> "終了"
なお、Exceptionには、例外の種類によっていくつものTypeがあり、例外タイプを指定することで、特定の例外だけをキャッチすることや、例外によって処理を変えることが可能です。(begin~endの間で、rescue節はいくつでも使用できます)
代表的な例外を紹介しておきます。
例外 | 代表的な原因 |
---|---|
IOError | ファイル入出力時のエラー |
IndexError | 配列などの要素番号が範囲外 |
RangeError | 値の範囲が定義外 |
RegexpError | 正しくない正規表現 |
TypeError | オブジェクトの型が違う |
ZeroDivisionError | 整数の0除算をしようとした |
RuntimeError | 不明なエラー |
また、メソッドの定義やクラス定義内のすべてで例外をキャッチする場合は、beginとendを省略することができます。
必ず行う処理を定義する(ensure)
前述のrescue節では、処理Aが複数行にわたる長い処理であっても、例外が発生した以降の処理は無理され、rescue節の処理が実行されるということです。
これは、例外発生時にその時点で処理が終了することがないため、終了処理を行うタイミングができ、とても安全な構造だと言えます。
しかし、終了の前に必ず行わなければいけない処理がある場合、少し不都合が起きます。それは、「処理が重複する」ということです。
例えば、ファイルに書き込む処理を行う場合、開いたファイルは必ず閉じなければいけません。そのため、通常処理の部分にはファイルを閉じる処理が記載されています。
しかし、前述のrescueだけを使った例外処理の場合、通常処理だけではなくrescue節にもファイルを閉じる処理を記載する必要があるわけです。
この程度の重複であれば大きな問題にならないかもしれませんが、正常終了するのに多数の処理があった場合などは、対応漏れや修正漏れのリスクが出てきてしまうでしょう。
そんなときにensureを利用します。ensureは、rescueの構文の中に含める形で、以下のように使用します。
begin 処理A rescue [=> 変数] 処理B(rescue節) [else 処理C] ensure 処理D(ensure節) end
ensure節があった場合、例外が発生しても発生しなくても、必ずensure節が実行されます。
つまり、先ほど説明した「必ず行わなければならない処理」をensure節に書いておけば、処理Aとrescue節に二重に記載する必要はありません。
先ほど紹介したrescue00-1.rbでは、「p("終了")」が重複しています。ensureを使えば、次のようになるでしょう。
rescue00-2.rb
peoples = [5,1,8,0,3] begin price = 1000 p("#{price}を割り勘すると...") peoples.each do |num| p(price / num) end rescue => ex p(ex) ensure p("終了") end
結果はrescue00-1.rbと同じです。
やり直す(retry)
Webサイトの応答待ちや、ユーザーからの入力待ちなど、エラーが発生しても改めてはじめから処理をやり直して継続したいということも、少なくありません。
そんなときには、retryを使用します。
rescue節内にretryがあった場合、処理がbeginまで戻り、改めてそこから処理をやり直してくれます。
ただし、必ず例外が発生する場合は、無限ループに陥ってしまうため、慎重に確認した上で使用しなければいけません。
わざと例外を発生させる(raise)
「わざと例外を発生させる」と言っても、おかしなプログラムコードを記載するということではありません。
Rubyのプログラムとしては正常であっても、処理として異常な場合にExceptionオブジェクトを生成して例外処理を行いたい場合があるのです。
例えば、メソッドを作っていて、ありえない引数が渡された場合にエラー終了させたい場合など、Exceptionオブジェクトを発生させることで、使用者に間違いに気づいてもらうわけです。
そういった場合に利用できるのは、raiseメソッドです。
raiseメソッドを使えば、Exceptionオブジェクトを生成して例外を発生させることができます。
代表的な構文は以下の3つです。
raise raise メッセージ or Exceptionオブジェクト raise 例外タイプ,メッセージ
何も与えなければ、RuntimeErrorを発生させます。
また、メッセージを表示したり、任意のExceptionオブジェクトを発生させたりすることも可能です。
rescue00-3.rb
peoples = [5,1,8,0,3] begin # エラーチェックを追加 if peoples.select { |n| n == 0}.size != 0 raise ZeroDivisionError, "0人はありえません" end price = 1000 p("#{price}を割り勘すると...") peoples.each do |num| p(price / num) end rescue => ex p(ex) ensure p("終了") end
(結果)
#<ZeroDivisionError: 0人はありえません> "終了"
rescue00-1.rbにエラーチェック処理を追加し、異常値があった場合はraiseメソッドを使って例外発生させる処理を追加しました。事前にチェックして、異常値の場合は例外を発生させているため、rescue00-1.rbでは処理が行われていく中で例外がキャッチされましたが、rescue00-3.rbでは、処理を始める前に例外をキャッチできています。
まとめ
今回は、例外処理について、説明しました。
rescue式を始めたとした例外処理は、理想的な環境や疑似的な入力を利用するため、学習中に使う機会は少ないかもしれません。
しかし、プログラムを異常終了させることなく継続運用したり、必要な終了処理を行わせてから終了したりする場合には、必須の処理です。つまり、異常な入力がいつ発生するかが分からない、実際の運用にあたってはとても重要な処理と言えるでしょう。
そのため、今からでも「例外を考慮したプログラム」を作る癖を作っておけば、実践を行うときにとてもスムーズに「動くシステム」を作ることができるようになります。
実際にrescueを使ったプログラムコードを常に書く必要はありませんが、意識していくようにしましょう。
・例外処理とは、「例外が発生することを前提として、例外が発生しても処理を継続できる、もしくは被害を最低限にして正常にプログラムを終了する」ための処理
・例外が発生して、rescue節へ処理がジャンプすることを「キャッチする」と言う
・rescueやensureなどで行う処理のかたまりを「節」と言う
・retryは容易に無限ループに陥るので、慎重に使うこと
DMM WEBCAMP ONLINE/プログラミングコースについて
WEBCAMPでは、「どこでも学べる」オンラインコースを開講しています。
初心者・未経験の方でもわずか1ヶ月でプログラミングの基礎を学び、アプリケーション開発をすることができます。
DMM WEBCAMP ONLINEが選ばれる3つの理由
1.学習の仕方がわからなくても安心!初心者に寄り添った学習カリキュラム
これから学習を始める初心者の方や、独学で挫折してしまった方に向けて、アプリケーション作成をゴールとする一気通貫したカリキュラムを提供しています。2500名を輩出しているWebCampだからこその教室でのリアルな受講生の声を反映し、オンラインコースでも初心者に寄り添った学習体験をご提供いたします。
2.つまずいたらすぐ解決!プロのエンジニアに質問し放題
プログラミング学習で挫折する理由の多くは「つまずいた時に聞ける人がいない」ことです。オンラインコースでは、現役エンジニアやプロ講師に、すぐにチャットでの質問が可能です。つまずきをすぐに解消できるので、挫折せずに学習を継続することができます。
3.1ヶ月間で終わらなくても大丈夫!カリキュラムは"卒業後"も"無料"で利用可能
「受講したいけど、仕事が急に忙しくなるかもしれないから受講するか迷っている・・・」そんな方も多いのではないでしょうか?ご安心ください。カリキュラムは期間終了後も「無料」で使うことができます。また、しっかり学習時間を確保した方も、振り返って学習することでプログラミングスキルがご自身に馴染んできます。
▼まずは無料でWEBデザインのカリキュラムを体験しよう!
【インタビュー】1ヶ月でRubyをゼロから学び、Webエンジニアとして転職!
ブラジルから帰国し技術をつけようとRubyエンジニアを目指してWebCampでRubyを学び、見事Webエンジニアとして転職を果たした田中さんにお話を伺いました。
「Rubyの学習がしたい。基礎をしっかりと理解したい」
「転職のサポートがほしい」
と考えている方はぜひお読み下さい。