Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guaranteed way to compare objects by identity in Ruby

Tags:

compare

ruby

What is a guaranteed way to compare two objects by their identity in Ruby? Given two variables, I want to return true if the variables point to the exact same object in memory.

For most Ruby objects, the equal? method compares by identity:

f = g = Object.new
p f.equal? g  # => true

However, this doesn't work for all objects. For example:

class F
  def ==(obj) false end
  def ===(obj) false end
  def eql?(obj) false end
  def equal?(obj) false end
  def object_id; self end
end

f = g = F.new
p f == g       # => false
p f === g      # => false
p f.eql? g     # => false
p f.equal? g   # => false
p f.object_id == g.object_id  # => false

What is a foolproof/guaranteed way of comparing two objects by identity which can't be defeated?

This is a purely intellectual question. The answer to any question that begins with "why" will probably be "Because I am curious."

like image 657
David Grayson Avatar asked Oct 25 '11 04:10

David Grayson


People also ask

What does == mean in Ruby?

The == operator, also known as equality or double equal, will return true if both objects are equal and false if they are not. str1 = “This is string”

What is Object_id in Ruby?

For every object, Ruby offers a method called object_id. You guessed it, this represents a random id for the specific object. This value is a reference of the address in memory where the object is store. Every object has a unique object id that will not change throughout the life of this object.

How do you compare values in two objects?

Whereas the equals() method compares two objects. Objects are equal when they have the same state (usually comparing variables). Objects are identical when they share the class identity. For example, the expression obj1==obj2 tests the identity, not equality.


1 Answers

You could grab an unbound version of Object#object_id, bind it to the object in question, and see what it says. Given your F class with one addition:

class F
  # ...
  def inspect; 'pancakes!' end # Just so we can tell what we have later.
end

Then:

>> f = F.new
>> f.object_id
=> pancakes!
>> unbound_object_id = Object.instance_method(:object_id)
>> unbound_object_id.bind(f).call
=> 2153000340
>> ObjectSpace._id2ref(2153000340).inspect
=> "pancakes!"

Of course, if someone opens up Object and replaces object_id then you're out of luck but this will be the least of your problems if someone does that. If you can grab your unbound_object_id UnboundMethod before anything else is loaded, then it won't matter if someone changes Object#object_id as your unbound_object_id will still be the original correct one.

So this round-about hack gives you a reliable object_id for any object (subject to the caveats above). Now you can grab and compare the object ids to get your reliable comparison.

like image 73
mu is too short Avatar answered Oct 14 '22 13:10

mu is too short