rubystructequalityclass-instance-variables

Why equality check for instance of Struct/Class are different?


I don't understand the difference between struct and class equality check. Since both Struct and Class gets their #hash from Kernel but they seem to behave differently.

I know that instance.hash will produce a different result for each class instance. Struct instance has different ancestors [Customer, Struct, Enumerable, Object, Kernel, BasicObject] compare to Class instance [Foo, Object, Kernel, BasicObject]. What really caused each Class instance to have a different hash number to othe

Customer = Struct.new(:name, :phone, :address) do

end

class Foo
  def initialize(the_name, phone, address)
    @name = the_name
    @phone = phone
    @address = address
  end
end


str_a = Customer.new('bond', 'ring', 'address')
str_b = Customer.new('bond', 'ring', 'address')

foo_a = Foo.new('bond', 'ring', 'address')
foo_b = Foo.new('bond', 'ring', 'address')

p str_a == str_b #true
p foo_a == foo_b #false

p str_a.hash # 4473040617195177332
p str_b.hash # 4473040617195177332
p foo_a.hash # -3118151143418428190
p foo_b.hash # -1042397847400824657

p str_a.method(:hash).owner #Kernel
p foo_a.method(:hash).owner #Kernel

both Struct and Class use Kernel for its hash_number generation. Why do a different instance of Class produce different hash int but Struct instance would produce the same hash int?


Solution

  • I believe the answer you're looking for is found in the Struct documentation

    Equality—Returns true if other has the same struct subclass 
    and has equal member values (according to Object#==).
    

    Your example has equal member values for str_a and str_b, and they have the same subclass (Customer), so they are equal when compared with ==

    Contrast this with the Object documentation

    Equality — At the Object level, == returns true only if 
    obj and other are the same object. Typically, this method is
    overridden in descendant classes to provide class-specific meaning.
    

    In your example, foo_a and foo_b are not the same object (because they're not the same instance)

    If you're seeking why these are different, I didn't really answer that question. Just that the behavior is as intended per the docs. They don't actually have the same ID:

    pry >> Bar = Struct.new(:name) do; end
    => Bar < Struct
    pry >> x = Bar.new
    => #<Struct:Bar:0x7f8ebca47610
            name = nil
    
    pry >> y = Bar.new
    => #<Struct:Bar:0x7f8ebca14058
            name = nil
    
    pry >> x.name = "foo"
    => "foo"
    pry >> y.name = "foo"
    => "foo"
    pry >> x
    => #<Struct:Bar:0x7f8ebca47610
            name = "foo"
    
    pry >> y
    => #<Struct:Bar:0x7f8ebca14058
            name = "foo"
    

    But, you'll note comparison is based on attributes, rather than the object ID:

    pry >> x == y
    => true
    

    Even though the object id's are different:

    pry >> x.__id__
    => 70125513489160
    pry >> y.__id__
    => 70125513383980