QA@IT

begin式全体の評価値にensure節の値を使わない理由は?

3400 PV

質問概要

begin式全体の評価値にensure節の値を使わない理由

  1. 実装上そうなっている
  2. メリットがあるのでそうしている

もし 2 の場合は「どういうメリットがあるか」も沿えて回答してもらえると嬉しいです.

疑問に至った経緯

通常,rubyでは最後に評価した値を評価値として用います.

def foo
  p 'foo'
  :foo
end

p foo
# => 'foo'
# => :foo

しかし,begin式の場合はensure節が最後に実行されるにも関わらず,ensure節の値は捨てられます.

def foo
  begin
    p 'begin'
    :begin
    raise
  rescue
    p 'rescue'
    :rescue
  ensure
    p 'ensure'
    :ensure
  end
end

p foo
# => "begin"
# => "rescue"
# => "ensure"
# => :rescue

るりまの制御構造にも

begin式全体の評価値は、本体/rescue節/else節のうち 最後に評価された文の値です。また各節において文が存在しなかったときの値 はnilです。いずれにしてもensure節の値は無視されます。

と記述があるので,設計通りの挙動のようです.

特に理由がなければ,通常の挙動と同様に,begin式の最後に評価した値(:ensure)を返すと思うのですが,そうなっていません.
何か理由があるため,このようになっていると考えましたが,その理由がわかりませんでした.

回答

理解したことをまとめます.

質問の回答としては「2.メリットがあるのでそうしている」である.

  • 大抵の場合,ensureは開いたリソースを閉じるのに使われる.
  • もし仮にensureの値を返すとすると,begin式にensure節を書いた場合,リソースを閉じた時の返り値(うまくリソースを閉じられたか)しか返せないことになる.
  • ensure節の結果を返さないことによって,通常の処理の結果を返せる.
def read_data file
  f = open(file)
  f.read # => ここの値を返したい(現状のRubyはそうなっている)
ensure
  f.close # => もしensureの値を返すとしたら,常にここの値が返る
end
編集 履歴 (0)

私は不思議に思わなかったのですが:

  • ensureは特別(特別だから特別なのだ)
  • ensureはbegin式を抜けるときに書く手続きを書く場所(値を返すものとは思わなかった)

nikuの質問を見て思ったのは、

ensure節の値を返すと、begin式の結果をみたときに、本体から抜けてきたのか、rescue節を抜けてきたのかをbegin式だけではわからなくなるなあ、と思いました。

「理由」としての回答になっていない気もしますが、以上、私が疑問を抱かなかった理由でした。

編集 履歴 (0)
  • 回答ありがとうございます.

    確かに,普通ensureを使うのは何かの後始末をする時で,その後始末がうまくいったかどうかについては興味ないですね.
    それよりも,通常ルートで終わったのか,例外ルートで終わったのかを返してくれた方が嬉しいです.
    なるほど.腑に落ちました.
    -
  • もしこれでも回答になっていたなら acceptお待ちしております :smile: -

返値についても,body 部分の返値のほうが大事そうですよね.

例:

  def read_data file
    f = open(file)
    f.read
  ensure
    f.close
  end
編集 履歴 (1)
  • ありがとうございます.よくわかりました.
    例を自分のまとめにも使わせてもらいました.
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。