💭
[Bug #20440] super にキーワード引数をフォワードするときの挙動に関するチケット
[Bug #20440] super
from child class duplicating a keyword argument as a positional Hash
- 次のように
super
に引数をフォワードする場合にキーワード引数ではなくて位置引数として渡っている、というバグ報告- Ruby 2.7 では期待する挙動になっていたが Ruby 3.0 から挙動が変わった
class Base
def foo(*args, x: 1)
"Base: calling foo with args: #{args}, x: #{x}"
end
end
class Child < Base
def foo(*)
# このときに foo に渡ってきた引数がそのまま親メソッドにフォワードされることを期待するがそのときにキーワード引数ではなくて位置引数として渡ってしまっている
super
end
end
pp Child.new.foo(0, x: 2)
# Ruby 2.7 => "Base: calling foo with args: [0], x: 2"
# Ruby 3.0 => "Base: calling foo with args: [0, {:x=>2}], x: 1"
- これは
foo(*)
のシグネチャが『位置引数の可変長引数』になっているのが原因で*
だと『位置引数』として引数を受け取る『位置引数を』super
でフォワードすることにある - なので次のように
foo(*, **)
とすることで『キーワード引数』を受け取りそれをフォワードすることができる
class Base
def foo(*args, x: 1)
"Base: calling foo with args: #{args}, x: #{x}"
end
end
class Child < Base
# * だけだと位置引数で受け取るが ** を付ける事でキーワード引数として値を受け取る事ができる
def foo(*, **)
super
end
end
pp Child.new.foo(0, x: 2)
# => "Base: calling foo with args: [0], x: 2"
- 他には
(...)
でも回避する事ができますね
class Base
def foo(*args, x: 1)
"Base: calling foo with args: #{args}, x: #{x}"
end
end
class Child < Base
# * だけだと位置引数で受け取るが ** を付ける事でキーワード引数として値を受け取る事ができる
def foo(...)
super
end
end
pp Child.new.foo(0, x: 2)
# => "Base: calling foo with args: [0], x: 2"
- ちなみに Ruby 2.7 と同じ挙動にしたい場合は
ruby2_keywords
が利用できます
class Base
def foo(*args, x: 1)
"Base: calling foo with args: #{args}, x: #{x}"
end
end
class Child < Base
ruby2_keywords def foo(*)
# このときに foo に渡ってきた引数がそのまま親メソッドにフォワードされることを期待するがそのときにキーワード引数ではなくて位置引数として渡ってしまっている
super
end
end
pp Child.new.foo(0, x: 2)
# => "Base: calling foo with args: [0], x: 2"
Discussion