Ruby例外処理の意外と知られていない仕様

例外処理を言語仕様に採用しているプログラミング言語は多い。

Rubyも例に漏れず、例外処理を採用しているわけだが、今回は、Rubyの例外処理について、誰もが知っているであろう基本レベルから少し掘り下げて、「意外と知っている人は少ないんじゃないか?」という部分の仕様について紹介する。

基本的な例外構文

まずは基本的な例外処理構文を紹介しておくことにする。

 

実行結果:

rescueセクション
ensureセクション

 

begin節でStandardErrorがraiseされたため、rescueで捕捉され、さらに例外の発生か否かにかかわらず実行されるensureセクションも実行される。

 

※余談だが、他の言語の例外文法に慣れ親しんでいると、begin~endの間に位置する、rescue、else、ensureはインデントしたくなるかもしれないが、どうやらRubyではこれらはインデントしないで使うのが一般的なようだ。

 

begin~rescueに位置するraise StandardError, “エラーをraise”の部分をコメントアウトして、例外が発生しないようにすると、rescue節の代わりにelse節が実行されるようになる。

 

実行結果:

elseセクション
ensureセクション

 

ここまでは例外処理の基本中の基本で、ひょっとしたら、いや多分、わざわざ紹介する必要も無かったかもしれない。

(つまらん!と思ってページを閉じてしまった人もいるかもしれないね)

 

ここから本題である、「意外と知っている人は少ないんじゃないか?」という部分に移行していくので、ここまで読んでくれた人は、ひき続き読み進んでほしい。

暗黙のrescueで捕捉されない例外

このコードを実行すると、どんな結果になるだろうか?

 

実行結果はこうだ。

sample_method ensureセクション
sample_methodから例外

 

sample_methodのbegin節の中で、例外をraiseしているにもかかわらず、sample_method内で定義されているrescue節もelse節も通っていない。

かといって例外がraiseされていないというわけではなく、sample_method呼び出し元で捕捉されていることがわかる。

 

sample_method内部での例外捕捉の記述はrescueのみであるのに対して、sample_method呼び出し側の例外捕捉の記述はrescue Exceptionであり、この違いが、raiseされたExceptionを捕捉できるか否かの違いを産んでいる。

 

このメカニズムは、単にrescueと記載しただけだと、StandardErrorクラスとそのサブクラスしか捕捉されないというRubyの言語仕様によるものだ。

ExceptionクラスはStandardErrorクラスのスーパークラスだから、rescueとしただけでは捕捉することはできない。

 

この言語仕様が実際に有意義に作用するコードを紹介する。

(サンプルコードの作成に手頃な例外クラスにInterruptクラスがあったため使用していますが、Interruptクラスは本来別のシチュエーションで使用するための例外クラスです)

execute_procメソッドは、渡されたブロックを実行し、実行後に”処理が完了しました。”というメッセージを出力してtrueを返すというメソッドだ。

それだけではProc.new {}.callとするのと大した違いはなく、execute_procメソッドの存在意義は無い。

しかしexecute_procメソッドは、Interruptがraiseされてブロックが中断された場合に、”処理がInterruptされました。”というメッセージを表示して、falseを返す、という実装ももっている。

 

実行結果:

1
2
3
処理がInterruptされました。

 

「StandardErrorでない例外は、rescueとしただけでは捕捉できない」という言語仕様があるから簡単に実装できたといえる。

beginとendは省略可能!?

rescue、else、ensure節は、そこに処理を記述する必要が無いならば、記述する必要が無い。つまりこれらは省略可能ということだ。

しかし、begin~endが省略可能ということを知っているだろうか?

 

具体的には、このように記述していたのが、

 

このように書き直すことができる。

つまりメソッド内では、beginと対になるendが省略できる。
(メソッドの外では省略することはできない)

公式マニュアル beginで説明されている文法では、beginとendは省略できないことになっているようだが・・・

 

実はこれを知ったとき、結構感動したりした。

 

 

2012/7/19 追記

 

こんな書き方もある。

結果はZeroDivisionErrorをrescueすることで、falseになる。

まとめ

“Ruby例外処理の意外と知られていない仕様” への1件のコメント

  1. b-m より:

    >> つまりメソッド内では、beginと対になるendが省略できる。
    これはbegin式の省略形ではなくdef式の構文ですよ.
    class式も同様にrescue節がかけます.

    def m
    [rescue節]
    [else節]
    [ensure節]
    end

    class C
    [rescue節]
    [else節]
    [ensure節]
    end

    なので,むしろ普段描いているdef式やclass式が
    rescue節を省略した形,と解釈する方が自然だと思います.

コメントを残す

トラックバック: http://pgnote.net/wp-trackback.php?p=174