【一人 bugs.ruby Advent Calendar 2021】[Feature #16806] Struct#initialize accepts keyword arguments too by default【13日目】

一人 bugs.ruby Advent Calendar 2021 13日目の記事になります。
Structkeyword_init: をデフォルトで有効にするというチケットです。

[Feature #16806] Struct#initialize accepts keyword arguments too by default

Structkeyword_init: をデフォルトで有効化させるチケットです。
対応後は以下のような挙動になる想定です。

User = Struct.new(:name, :age)

# これは以前の挙動のまま
mami = User.new("mami", 14)
pp mami
# => #<struct User name="mami", age=14>

# キーワード引数を渡すと keyword_init: true と同じように初期化される
homu = User.new(name: "homu", age: 14)
pp homu
# => #<struct User name="homu", age=14>

ただし keyword_init: false の場合でも User.new(name: "homu", age: 14) の用に渡せる事ができます。

User = Struct.new(:name, :age, keyword_init: false)

# キーワード引数ではなくて User.new({ name: "homu", age: 14 }) と同じ意味になる
# なので現状だと name に Hash オブジェクトが入ってしまうので既存の挙動と変わってしまう
p User.new(name: "homu", age: 14)
# => #<struct User name={:name=>"homu", :age=>14}, age=nil>

デフォルトで keyword_init: true にした場合に上のようなコードの意味が変わってしまい非互換な変更になってしまいます。
なので Ruby 3.1 ではまず keyword_init: false に対してキーワード引数を渡すと警告を出すようにし、Ruby 3.2 で変更するような移行パスになります。

User = Struct.new(:name, :age)

# Ruby 3.1 では keyword_init: true でない場合にキーワード引数を渡すと警告が出る
# warning: Passing only keyword arguments to Struct#initialize will behave differently from Ruby 3.2. Please use a Hash literal like .new({k: v}) instead of .new(k: v).
p User.new(name: "homu", age: 14)
# => #<struct User name={:name=>"homu", :age=>14}, age=nil>

# 明示的に Hash を渡した場合は警告は出ない
# no warning
p User.new({ name: "homu", age: 14 })

結果的に非互換にはなるんですがこれはよさそうな変更ですね。