💬

Ruby 3.4 から **nil が許容されるようになった + ちょっとした非互換

2024/03/03に公開

Ruby 3.4 で **nil が許容されるようになりました。
これにより以下のような **obj をするときに objnil であるかどうかを気にする必要がなくなります。

opt = nil

# opt が nil でも問題ない
# この場合は **{} と同等になる
func(**opt)
# Ruby 3.4 => error: no implicit conversion of nil into Hash (TypeError)
# Ruby 3.4 => OK

nil.to_hash が定義されたわけではない

** 構文は内部で #to_hash メソッドが呼ばれて Hash として変換されます。
なので #to_hash が定義されているオブジェクトであれば ** が利用できます。

class X
  def to_hash
    { value: 42 }
  end
end

x = X.new

# ** すると内部で #to_hash が呼ばれ、その結果に置き換えられる
pp(**x)
# => {:value=>42}

なので nil.to_hash が定義されていれば Ruby 3.4 でも **nil は利用できるんですが、今回の対応では特に nil.to_hash が追加されたわけではなくて内部処理として処理されるようになった感じです。

nil.to_hash
# Ruby 3.3 => undefined method `to_hash' for nil (NoMethodError)
# Ruby 3.4 => undefined method `to_hash' for nil (NoMethodError)

更に言うと Ruby 3.4 からは『 nil.to_hash が呼び出されなくなった』とも言えそうです。

class NilClass
  def to_hash
    pp "Hoge"
    {}
  end
end

{ **nil }
# Ruby 3.3 => "Hoge"
# Ruby 3.4 => no output

なので Ruby 3.4 以前で『 **nil を呼び出したときに nil.to_hash が呼び出されていること』を期待しているコードがあれば非互換になってしまうので注意する必要があります(まあ無いとは思いますが…。

参照

GitHubで編集を提案

Discussion