rescue、ensureでの注意
あまり意識して使っていなかったのですが、意識せざるをえない状況になってしまったので、あとで「あれって何だっけ?」とならないためのメモを書いておきます。
例外処理を使うときの注意
こういうことはわりとよくやると思います。(こんな単純なことはさすがにしませんが)
def test(div) 100 / div rescue 0 end
意図するところは「divがゼロじゃないときは 100を divで割った値が返ってきて、ゼロのときはゼロが返ってくる」というものです。これは何の問題もなく動きます。
では「divが 100未満だったら割った値を返して、それ以外はゼロを返す」というときはどうするでしょうか。100以上のときもゼロのときもゼロを返すんだから、こう書きたくならないでしょうか?
def test(div)
return 100 / div if div < 100
rescue
ensure
0
end
これで、test(200)とか test(0)とやると、nilが返ってきます。そういう風にしたければ、こうしないといけません。
def test(div) r = 0 r = 100 / div if div < 100 rescue ensure return r end
理由
どうしてでしょう?
begin式全体の評価値は、本体/rescue節/else節のうち最後に評価された文の値です。また各節において文が存在しなかったときの値はnilです。いずれにしてもensure節の値は無視されます。
http://www.ruby-lang.org/ja/man/html/_C0A9B8E6B9BDC2A4.html#a.ce.e3.b3.b0.bd.e8.cd.fd
この通りで、ensure節の値は無視されるので、returnを使わずに書くとすればこうなります。
def test(div) return 100 / div if div < 100 0 rescue 0 end
みっともないですね。「ensure節の値が無視される」ことをわかった上で、return を使って書いたほうが自分好みです。
あと、勘違いしていましたが、ensure節に returnを書くと、本体で正常に returnするケースでも ensure節で returnしてしまいます。
def test(div) return 100 / div if div < 100 rescue ensure return 0 end
こういう書き方をしたらダメということです。