📌

[Bug #20307] compare_by_identity と Hash#update のバグ報告

2024/03/30に公開

[Bug #20307] Hash#update from compare_by_identity hash can have unfrozen string keys

  • 以下のように compare_by_identity な Hash を Hash#update で取り込むと意図しない挙動になるというバグ報告
    • どうやってみつけるのこれ…
key = "a"
hash1 = {}.compare_by_identity
hash1[key] = 0
hash2 = {}.update(hash1)

pp hash1   # => {"a"=>0}
pp hash2   # => {"a"=>0}

pp hash1.compare_by_identity?   # => true
pp hash2.compare_by_identity?   # => false

key.upcase!

# key は大文字の "A" として扱われているが
pp hash2   # => {"A"=>0}
pp hash2.keys   # => ["A"]

# 添字アクセスなどはできない状態になっている
pp hash2[key]   # => nil
pp ((hash2.fetch(key) rescue $!))
# => #<KeyError: key not found: "A">

# compare_by_identity と同じような挙動になる
hash2["A"] = 1
pp hash2
# => {"A"=>0, "A"=>1}
  • この問題は開発版の Ruby 3.4 で修正済み
  • ちなみに Hash#compare_by_identity はレシーバの Hash のキーの一致判定を『オブジェクトの同一性で判定する』ように変更するメソッド
hash = { "a" => "homu" }

# これは `"a" のキーが同じオブジェクトとして判定されるので上書きされる
hash["a"] = "mami"
pp hash   # => {"a"=>"mami"}

hash.compare_by_identity

# compare_by_identity すると object_id で比較されるようになるので上書きされない
hash["a"] = "mado"
pp hash   # => {"a"=>"mami", "a"=>"mado"}
GitHubで編集を提案

Discussion