mistercake
mistercake

Reputation: 723

Matching hashes, eql? returns true, but has_key? returns false

I'm on Ruby 2.2.1 and have the following situation:

a = ... # some object
h = ... # some hash

p h.size #=> 1
p h.keys.first.hash == a.hash #=> true
p h.keys.first.eql?(a) #=> true
p h.has_key?(a) #=> false

How is this possible? I thought that the hashes matching and eql? returning true were the only conditions for keys to be considered equal.


Edit: Here's the full program. But please note that I'm not asking how to fix it—I know how. I'm asking why Ruby behaves that way! Because I'm confused as to why the API contracts of Hash count for nothing in this situation.

class A
  attr_reader :x

  def initialize(x)
    @x = x
  end

  MY_HASH = { A.new(5) => 'foo' }

  def ==(other)
    @x == other.x
  end

  alias_method :eql?, :==

  def hash
    @x
  end
end

a = A.new(5)
h = A::MY_HASH

p h.size #=> 1
p h.keys.first.hash == a.hash #=> true
p h.keys.first.eql?(a) #=> true
p h.has_key?(a) #=> false

Upvotes: 2

Views: 223

Answers (1)

hirolau
hirolau

Reputation: 13901

At the time when you create MY_HASH the new hash function of A is not yet defined so MY_HASH will use the default one when creating an index of it's values. When you later define a new hash function it will change how the objects are hashed BUT NOT AUTOMATICALLY update the index in the already existing Hash MY_HASH.

You solve this my initializing MY_HASH after you have defined the new hash method for class A or by running MY_HASH.rehash

p h.has_key?(a) #=> false
A::MY_HASH.rehash
p h.has_key?(a) #=> true

Upvotes: 3

Related Questions