Let’s say you are building a leetspeak that deals with w00ts. You might write a class that looks like this:
class Woot def ==(other) true end end
In theory, any Woot is equal to anything else:
puts Woot.new == Woot.new # true
You might think that with this setup, you could do something like this:
x = [ Woot.new ] y = [ Woot.new ] z = x - y
You might expect z to be an empty array in the case, but oh how wrong you would be. In the example above, the == is never called at all.
After reading through the docs on Array, overriding all 4 equals method in ruby (eql?, equal?, ==, ===) and overriding object_id you will still not be able to make it work.
A quick look through the C code shows that Array#- hashifies the array. The hashing algorithm looks for a method called “hash” on the object. If that’s not there, it uses the memory id. Great, so now we can just add hash and be done, like this:
class Woot def ==(other) true end def hash 1 end end
Wrong! You still don’t get an empty array. It turns out that the hash algorithm calls eql?, not ==, so you have to make sure to alias that as well, like so:
class Woot def ==(other) true end alias :eql? :== def hash 1 end end
So if you want normal array semantics for your objects, be sure to provide #hash, #== and #eql?.
About the Author