オープンソースのオブジェクト指向プログラミング言語「Ruby」の文法を一から学ぶための入門連載。最新版の2.1に対応しています。今回は、例外とその補足について、begin、rescue、end、ensure、else、retry、後置rescueなどの基本的な使い方を交えて解説します。
連載8回目に当たる前回の「Rubyの面白さを理解するためのメソッド、ブロック、Proc、lambda、クロージャの基本」では、メソッドやブロックといった仕組みについて、さらに掘り下げて解説しました。前回学んだ知識は、メタプログラミングを学ぶための基礎となります。
連載9回目の今回は、他のオブジェクト指向言語でもポピュラーな仕組みである例外について学びます。例外の仕組みをうまく使うことによって、あるべきファイルが存在しない場合や、RubyプログラムからHTTPを使ってデータを取得できない場合などに、適切に対処できるようなプログラムを書くことができます。
多くのオブジェクト指向言語では、「例外」とその「捕捉」という仕組みを使うことで、エラーが発生した場合の処理を記述できます。ここでいうエラーとは、存在しないファイルを開こうとするなど、実行時の状況に依存するようなエラーです。また、例外の仕組みを利用することで、「メソッドが期待しないような引数が渡されたときに例外を投げてプログラマーに知らせる」というようなこともできます。
例外を使うと、オブジェクトとしてエラーに関する情報を扱えるので、エラー処理を簡潔に分かりやすく記述できます。一方で、C言語におけるgoto文のように「処理の流れをねじ曲げてしまう」という側面もあるので、用法用量を守ってお行儀良く使う必要があります。
数学においては、「ある数を0で割ることはできない(定義されていない)」とします。Rubyでも同様に、整数は0で割ることはできず、いわゆる「ゼロ除算エラー」となります。pryで確かめてみましょう。
[1] pry(main)> 42 / 0 ZeroDivisionError: divided by 0 from (pry):1:in `/'
期待通りエラーが出ました。2行目の「ZeroDivisionError」は、どのような種類のエラーであるかを表す例外クラスです。Rubyでは全てがオブジェクトで成り立っているので、例外の種類に応じて多くのクラスが定義されています。続く「divided by 0」というのは、発生したエラーを端的に説明するメッセージです。
3行目では、どのメソッドの実行時に例外が発生したのかを教えてくれています。この場合は、「/」メソッド(連載第2回で紹介しましたが、Rubyでは演算子もメソッドであることを思い出してください)で例外が発生したことを知らせてくれています。
クラスを設計するときに、プログラマーが任意に例外を発生させたいこともあるでしょう。典型的な例としては、作成したメソッドの引数が非負の整数であることを期待するのに、負の整数が渡されてきた場合などです。
このような場合には、Kernelモジュールで定義されている「Kernel.raise」メソッドを利用します。通常、Kernelモジュールのメソッドは「Kernel.」を省略できるので、以下のように例外を発生できます。
[1] pry(main)> raise "something wrong" RuntimeError: something wrong from (pry):1:in `__pry__'
2行目で、raiseメソッドの引数に与えた文字列がそのままメッセージとして利用されているのが分かります。また、エラーの種類を示す例外クラスはRuntimeErrorです。RuntimeErrorは、数ある例外クラスのどれにも属さないようなエラーを表す、いわば汎用的な例外クラスです。
ここまで、ZeroDivisionErrorとRuntimeErrorを紹介しましたので、この勢いで主要なRuby組み込みの例外クラスを一覧として以下にまとめます。詳細は、「Rubyリファレンスマニュアル - 組み込みクラス」を確認してください。
・Exception ├・NoMemoryError ├・ScriptError │└(スクリプトの実行に関するいくつかのエラーが含まれます) ├・SecurityError ├・SignalException │└Interrupt ├・StandardError │├・ArgumentError │├・EncodingError ││└(文字エンコーディングに関するいくつかのエラーが含まれます) │├・FiberError │├・IOError ││└・EOFError │├・IndexError ││└(添字に関するいくつかのエラーが含まれます) │├・LocalJumpError │├・Math::DomainError │├・NameError ││└・NoMethodError │├・RangeError ││└・FloatDomainError │├・RegexpError │├・RuntimeError │├・SystemCallError ││└(システムコールなどで発生する多くのエラーが含まれます) │├・ThreadError │├・TypeError │└・ZeroDivisionError ├・SystemExit ├・SystemStackError └・fatal
全ての例外はExceptionクラスを継承しています。中でも注目すべきは、StandardErrorクラスです。StandardErrorクラスを継承する各例外クラスは、アプリケーションレベルの例外です。StandardErrorクラスを継承しない例外クラス(例えばNoMemoryErrorなど)は、システムレベルの例外です。
このように、アプリケーションレベルでの例外とシステムレベルでの例外を区別できるような継承関係にしておくことで、例外を捕捉するときに役立ちます。もし、「メモリが足りない」などのシステムレベルの例外を捕捉してしまっても、アプリケーションレベルでできることは非常に限られるからです。
Copyright © ITmedia, Inc. All Rights Reserved.