【一人 bugs.ruby Advent Calendar 2020】[Feature #17004] Provide a way for methods to omit their return value【22日目】

一人 bugs.ruby Advent Calendar 2020 22日目の記事になります。

[Feature #17004] Provide a way for methods to omit their return value

このチケットは任意のメソッドが戻り値を受け取るか受け取らないかを判定するメソッドの追加を追加するという内容のチケットです。
ということかというと RubyVM.return_value_is_used? というメソッドを追加し、これをメソッド内で呼び出すと

  • 戻り値を受け取る場合: true を返す
  • そうでない場合: false を返す

というような判定を行うことができます。
具体的に言うとこんな感じで判定する事ができます。

def hoge
  if RubyVM.return_value_is_used?
    pp "戻り値を受け取る"
  else
    pp "戻り値を受け取らない"
  end
end

hoge          # "戻り値を受け取らない"
value = hoge  # "戻り値を受け取る"
Array hoge    # "戻り値を受け取る"
hoge.nil?     # "戻り値を受け取る"

# 最後に呼び出したやつも?
hoge          # "戻り値を受け取る"

この判定メソッドを利用すると次のように『戻り値を受け取らない場合は無駄な処理を省く』事ができます。

class Hash
  def refresh(key)
    # 引数を受け取る場合のみ result を設定する
    if RubyVM.return_value_is_used?
      result = self[key]
    end
    self[key] = nil
    result
  end
end

homu = { name: "homu", age: 14 }
homu.refresh(:name)
p homu
# => {:name=>nil, :age=>14}

age = homu.refresh(:age)
pp homu  # => {:name=>nil, :age=>nil}
pp age   # => 14
class User < ActiveRecord::Base
  def update_name(name)
    update!(name: name)

    # reload した値を返す
    reload if RubyVM.return_value_is_used?
  end
end

これは面白いアプローチですね。
ただし、次のように戻り値になる場合は『戻り値を受け取る』ことになるので注意。

def hoge
  if RubyVM.return_value_is_used?
    pp "戻り値を受け取る"
  else
    pp "戻り値を受け取らない"
  end
end

def foo
  hoge
end
foo    # "戻り値を受け取る"

def bar
  hoge
  nil
end
bar    # "戻り値を受け取らない"

便利そうっちゃ便利そうだけどメソッドごとに RubyVM.return_value_is_used? で処理を分岐するのはめっちゃきつそう…。
実際には極端に重くなるようなメソッドぐらいで使いそうな気がするけど…どうだろうか。

参照