ruby hash as key to a hash

2.7k views Asked by At

Came across the following weird behaviour in ruby 1.8.6, in 1.8.7 it seems to be working correctly. Does anyone know what would have caused this?

h = {}
key_1 = {1 => 2}
key_2 = {1 => 2}
h[key_1] = 3
p key_1 == key_2 # => true
p h.has_key?(key_2) # => expect true, get false, wtf?

I had thought that it would be caused by the implementation of the hash method on the Hash class.

p [key_1.hash, key_2.hash] # => [537787070, 537787060] (different)

but even if I override the hash method of Hash

class Hash
  def hash
    return self.keys.hash + self.values.hash
  end
end

p [key_1.hash, key_2.hash] # => [8,8] (same
p h.has_key?(key_2)        # => false

codepad link to online ruby 1.8.6 interpreter results: http://codepad.org/7nCYMP4w

2

There are 2 answers

6
Michael Papile On BEST ANSWER

The answer is because in Ruby 1.8.6 the hash coding algorithm was broken for hash keys.

http://paulbarry.com/articles/2009/09/14/why-rails-3-will-require-ruby-1-8-7

Edit: Here is an example that shows that ruby does not call .hash internally:

 class Hash
    def hash
       raise
    end
 end

 {1=>1}.hash
 RuntimeError: 
from (irb):12:in `hash'
from (irb):17

 h = {1=>2}
 {1=>2}
 h[1]
 2

Ruby 1.8.6 is broken in this respect, and if there were a pure Ruby way to do it (such as opening Hash, people would do it. It was fixed in 1.8.7

0
rogerdpack On

This is fixed in 1.8.7+ but you can monkey patch 1.8.6 to do it right, too ex: https://github.com/rdp/sane/blob/master/lib/sane/hash_hashes.rb