Hash#key? を上書きしてもキーワード引数は上書きできない

Ruby のしくみに面白いコードが書かれていました。

class Hash
  def key?(val)
    puts "Looking for key #{val}"
    false
  end
end

def add_two(a: 2, b: 3)
  a+b
end

puts add_two(a: 1, b: 1)
# Looking for key a
# Looking for key b
# 5

キーワード引数は内部で Hash#key? を使っているので、上書きすると挙動が変わるという内容。

面白そうなので、実際に試してみた。

Ruby 2.6.0-dev

サンプルコードを写経して、 2.6.0-dev で動かしてみた。

$ ruby test.rb
2

あれ、結果が違う・・・。 dev じゃなくて、最新の 2.5 系なら動くのかな?

Ruby 2.5.1

$ ruby test.rb
2

これも動かないのか・・・。 過去バージョンのどこかで挙動が変わったのかな。

どのバージョンで変わったのか調べてみた

# 2.4.4
2

# 2.3.7
2

# 2.2.10
2

# 2.2.0
2

# 2.1.10
Looking for key a
Looking for key b
5

2.2系で仕様が変わったっぽい。

変更のコミットを探してみた

git log --no-merges -p v2_1_0..v2_2_0 から関係ありそうなコミットを探してみました。

関係ありそうなコミットを探したところ、 ko1 さんが keyword argument 周りを大きく書き換えているコミットがあったので、これで挙動が変わったのかも?(自信はない)

https://github.com/ruby/ruby/commit/fbebd502f9f374d1eef31c63c10c7d8adcd63280

まとめ

書籍を読んだあと、自分で試すの大事ですね。

もっと Ruby の面白い挙動を知りたい人は「Rubyのしくみ」を読むのがオススメです。

Rubyのしくみ -Ruby Under a Microscope-

Rubyのしくみ -Ruby Under a Microscope-