【Ruby】エラー処理と例外処理

「はじめてのRuby」を読んでのメモ。

今回は第10章「エラー処理と例外処理」


エラー処理について

プログラムの実行中に発生するエラーには次のようなものがある。

  • データのエラー

金額があるべき欄に商品名が書いてあり計算ができない、など。HTMLのような構造を持ったデータの場合は、タグが閉じられていないといった構文上の間違いも含む。

  • システムのエラー

ハードディスクの故障、CDがドライブに挿入されてないといったプログラムの力だけでは回復できない問題。

  • プログラムのミス

存在しないメソッド呼び出し、引数として渡す値の誤りなど、プログラムのミスによるエラー。


エラーには何らかの対処が必要である。

  • エラーの原因を取り除く
  • 無視して続行
  • エラー発生前の状態を復元
  • もう一度試す
  • プログラムを終了する



実際どのような対処を行えばよいかは環境によって異なる。しかし、エラーの発生があらかじめ予想される場合には、

  • 入力データ、特に人間が手作業で作成したデータを破壊しないか
  • エラーの内容と可能ならその原因を通知できるか

これらの点に気をつけておく。

例外処理

プログラム実行中にエラーが起こると例外が発生する。このとき、プログラムの実行は一時中断し、例外処理を探す。例外処理が記述されていればそれを実行する。記述されていない場合は、

ファイル名:行番号:in 'メソッド名':エラーメッセージ (例外クラス名)
    from ファイル:行番号:in メソッド名    # エラー発生箇所の呼び出し元
    .
    .
    .

このようなメッセージを表示してから終了する。

例外処理には次のようなメリットがある。

  • 操作の完了を1つ1つ完了しなくてもエラーは自動的に検出される
  • エラーの発生場所も同時に報告されるためデバッグしやすい
  • 正常な処理とエラーの処理を分けて記述でき、プログラムの見通しがよくなる


例外処理の書き方

begin
  例外を発生させる可能性のある処理
rescue
  例外が起こった場合の処理
end

rescue に続けて変数名を指定することで例外オブジェクトを得ることが出来る。

begin
  例外を発生させる可能性のある処理
rescue => 例外オブジェクトが代入される変数
  例外が起こった場合の処理
end

変数名を指定しなくても変数 $! に自動的にセットされる。

  • $! ... 最後に発生した例外
  • $@ ... 最後に発生した例外の位置に関する情報



例外オブジェクトからメソッドを呼ぶことで例外に関する情報を取得できる。

  • class ... 例外の種類
  • message ... 例外のメッセージ
  • backtrace ... 例外の発生した位置に関する情報($@ = $!.backtrace)


後処理

ensure 節に処理を書くと、例外の有無に関わらず常に実行される。

begin
  例外を発生される可能性のある処理
rescue => 変数
  例外が起こった場合の処理
ensure 
  例外の有無に関わらず実行される処理
end

ファイルの操作をするとき、処理がうまくいくかどうかに関わらずファイルを閉じなければならない、といったときに使用する。

やり直し

rescue 節で retry を用いると、 begin 以下の処理をもう一度やり直す。

file = ARGV[0]
begin
  io = File.open(file)
rescue
  sleep 10
  retry
end

data = io.read
io.close

場合によっては無限ループになるため注意。

rescue 修飾子

# 数値として不正な文字列を受け取った場合、例外を返す
n = Integer(val) rescue 0

# 同様の意味
begin 
  n = Integer(val)
rescue
  0
end

難しい処理が必要でないときにデフォルト値が欲しい、などの場合に使われる。

例外処理の構文の補足

メソッドの処理全体を begin ~ end でくくる場合に、 begin と end を省略して rescue 節や ensure 節を記述できる。
クラス定義内でも同様のことができるが、例外が発生した箇所以降のメソッド定義などが行われなくなるため、通常は利用しない。

補足する例外を指定する

# 複数の例外に対してそれぞれ個別に対処

begin
  例外を発生させる可能性のある処理
rescue Exception1, Exception2 => 変数
  Exception1 または Exception2 に対する処理
rescue Exception3 => 変数
  Exception3 に対する処理
rescue
  それ以外の例外が起こった場合の処理

クラスを指定すれば、想定している例外だけを補足できる。

file1 = ARGV[0]
file2 = ARGV[1]
begin
  io = File.open(file1)
rescue Errno::ENOENT, Errno::EACCES
  io = File.open(file2)
end

Errno::ENOENT ファイルが存在しない場合、Errno::EACCESS はファイルを開くための権限がない場合に発生する例外である。

例外を発生させる

自分で例外を発生させるには raise メソッドを使う。

  • raise メッセージ

RuntimeError を発生させる。新しく生成された例外オブジェクトにメッセージとして文字列をセットする。

  • raise 例外クラス

指定した例外を発生させる。

  • raise 例外クラス, メッセージ

指定した例外クラスを発生させ、新しく生成された例外オブジェクトにメッセージとして文字列をセットする。

  • raise

rescue 節の外では RuntimeError を発生させ、rescue 節の中では最後に発生した例外($!)をもう一度発生させる。