tags:

views:

96

answers:

3

For a simple struct-like class:

class Tiger
  attr_accessor :name, :num_stripes
end

what is the correct way to implement equality correctly, to ensure that ==, ===, eql?, etc work, and so that instances of the class play nicely in sets, hashes, etc.

EDIT

Also, what's a nice way to implement equality when you want to compare based on state that's not exposed outside the class? For example:

class Lady
  attr_accessor :name

  def initialize(age)
    @age = age
  end
end

here I'd like my equality method to take @age into account, but the Lady doesn't expose her age to clients. Would I have to use instance_variable_get in this situation?

A: 

Usually with the == operator.

def == (other)
  if other.class == self.class
    @name == other.name && @num_stripes == other.num_stripes
  else
    false
  end
end
The Wicked Flea
Thanks. However I think that if you only define == then instances of the class won't behave as expected in hashes and sets.
Pete Hodgson
Also, I /think/ that == isn't supposed to check type equality (as your example is doing). That's what eql? is supposed to do. Could be wrong on that tho.
Pete Hodgson
The behavior only varies if you make it vary, Pete. Last I checked `true == true` (and `1+1 == 2`) still returns `true`...
The Wicked Flea
+3  A: 

This is a nice writeup comparing the ins and outs of defining object equality

ennuikiller
So my take from that article is that I'd need to define ==, and also eql? and hash if I wanted instances of my class to work correctly in sets and hashes?
Pete Hodgson
+1  A: 

To simplify comparison operators for objects with more than one state variable, create a private method that returns all of the object's state as an array. Then just compare the two states:

class Thing

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end

  def ==(o)
    o.class == self.class && o.send(:state) == state
  end
  alias_method :eql?, :==

  private

  def state
    [@a, @b, @c]
  end

end

p Thing.new(1, 2, 3) == Thing.new(1, 2, 3)    # => true
p Thing.new(1, 2, 3) == Thing.new(1, 2, 4)    # => false
Wayne Conrad
I really like this trick of comparing objects using by delegating comparison to the state array.
Pete Hodgson