🙆

[Bug #20640] Ruby 3.0 から **kwd しつつ kwd.delete したときの挙動が壊れているバグ報告

2024/07/19に公開

[Bug #20640] Evaluation Order Issue in f(**h, &h.delete(key))

  • Ruby 3.0 からキーワード引数で Hash を渡しつつブロック引数で Hash#delete を呼び出したときの評価順が壊れているというバグ報告
  • 以下のように **hass しつつ &hash.delete すると Ruby 3.0 から挙動が変わります
def check(**kwd, &block)
  kwd
end

kwd = { a: proc {} }

# kwd をキーワード引数で渡しつつ、ブロック引数で #delete した値を渡す
# Ruby 3.0 以降では delete した後の値が kwd に渡される
pp check(**kwd, &kwd.delete(:a))
# Ruby 3.0 以前 => {:a=>#<Proc:0x00007c0154fa8f58 /tmp/vrD7Av9/27:6>}
# Ruby 3.0 以降 => {}
  • また位置引数がある場合は Ruby 3.3 から挙動が変わります
def check(*args, **kwd, &block)
  kwd
end

args = []
kwd = { a: proc {} }

# こっちは位置引数も渡す
pp check(*args, **kwd, &kwd.delete(:a))
# Ruby 3.3 以前 => {:a=>#<Proc:0x00007c0154fa8f58 /tmp/vrD7Av9/27:6>}
# Ruby 3.3 以降 => {}
  • Ruby では * ** する前に値をコピーして渡すのが期待する挙動なので以前と同じような挙動になるように修正される予定
def check(*args, **kwd, &block)
  kwd
end

kwd = { a: proc {} }

# 以下が期待する挙動になる
pp check(**kwd, &kwd.delete(:a))
# {:a=>#<Proc:0x00007c0154fa8f58 /tmp/vrD7Av9/27:6>}


args = []
kwd = { a: proc {} }

# こっちは位置引数も渡す
pp check(*args, **kwd, &kwd.delete(:a))
# {:a=>#<Proc:0x00007c0154fa8f58 /tmp/vrD7Av9/27:6>}
GitHubで編集を提案

Discussion