ハッシュを再計算するHash#rehash

RubyでHashのKeyにArrayやHashを使ってみたところ、値が取り出せなくなった。

irb(main):001:0> a = ["a","b"]
=> ["a", "b"]
irb(main):002:0> c = ["c","d"]
=> ["c", "d"]
irb(main):003:0> h = {a => 100, c => 200}
=> {["a", "b"]=>100, ["c", "d"]=>200}
irb(main):004:0> h[a]
=> 100
irb(main):005:0> h[["a","b"]]
=> 100
irb(main):006:0> a[0] = "e"
=> "e"
irb(main):007:0> h[a]
=> nil
irb(main):008:0> h
=> {["e", "b"]=>100, ["c", "d"]=>200}
irb(main):009:0> a
=> ["e", "b"]
irb(main):010:0> h.rehash
=> {["e", "b"]=>100, ["c", "d"]=>200}
irb(main):011:0> h[a]
=> 100
irb(main):012:0> 

↑の例では6行目でキーにしているオブジェクトaの値を書き換えたところ、7行目でオブジェクトaをキーにしているハッシュがnilを返すようになる。
しかしハッシュの中身を見てみると、一見正しいように見えるが、このキーにはアクセスできない。

irb(main):012:0> h[a]
=> 100
irb(main):013:0> a[0] = 1
=> 1
irb(main):014:0> h[a]
=> nil
irb(main):015:0> h
=> {[1, "b"]=>100, ["c", "d"]=>200}
irb(main):016:0> h[a] = 100
=> 100
irb(main):017:0> h
=> {[1, "b"]=>100, [1, "b"]=>100, ["c", "d"]=>200}
irb(main):018:0> h.rehash
=> {[1, "b"]=>100, ["c", "d"]=>200}

一見同じキーが2個出来たりする。

こんな風に、オブジェクトをキーにしていてオブジェクトが書き換わった場合はrehashをすると、正しくアクセスできるHashに戻るようだ。

irb(main):027:0> h
=> {[2, "b"]=>300, [2, "b"]=>100, ["c", "d"]=>200}
irb(main):028:0> h.rehash
=> {[2, "b"]=>100, ["c", "d"]=>200}

同じキーで別の値を持ってる状態でrehashすると、片方は消えた。

http://www.ruby-lang.org/ja/man/html/Hash.html#rehash
http://www.ruby-doc.org/docs/ProgrammingRuby/html/ref_c_hash.html#Hash.rehash