In Ruby, what is the difference between ==, eql? and equal?, and why is 1 == 1.0 true while 1.eql?(1.0) is false?
answer
- three questions, three strictness levels
- identity: same object_id
- value: overridden per class
- hash-key equality, no conversion
- {1 => :a}[1.0] is nil
basics
~20 sequal? asks whether both are the same object; == compares values and is overridden per class; eql? is the stricter hash-key equality. Numeric == compares across types, so 1 == 1.0, but eql? requires the same class.
solid answer
~40 s`equal?` is **identity**: true only for the very same object (same `object_id`), and it should never be overridden. `==` is **value equality**, which classes override: two separately built strings with the same characters are `==` but not `equal?`. `eql?` is the equality `Hash` uses for keys (together with `hash`); for most classes it matches `==`, but numeric classes make it stricter. `Integer#==` compares across numeric types, so `1 == 1.0` is `true`, while `eql?` also requires the same type, so `1.eql?(1.0)` is `false`. The practical effect: `{1 => :one}[1.0]` returns `nil`, and `[1, 1.0].uniq` keeps both elements. On a plain `Object`, all three compare identity until a class overrides them.
code
ruby · 11 linesa = "ruby"
b = a.dup
a == b # => true same characters
a.eql?(b) # => true String#eql? also compares content
a.equal?(b) # => false two different objects
a.object_id == b.object_id # => false
1 == 1.0 # => true
1.eql?(1.0) # => false
{1 => :one}[1.0] # => nilgo deeper
State what each of the three asks: same object, same value, same hash key; give 1 == 1.0 versus 1.eql?(1.0) as the example.
Tie eql? and hash to Hash lookups and uniq, explain the numeric exception, and know that plain objects compare by identity in all three.
Spot lookups that miss because a key arrived as 1.0 instead of 1, or as a copy of a value object without eql? and hash, and fix the key type or the class.
Set the rule for value classes in a codebase: prefer Data.define or override ==, eql? and hash together, and never let equality depend on mutable state.
## Three kinds of sameness Ruby gives every object three equality methods, each answering a different question: | Method | Question it answers | Default on Object | Typically overridden? | |---|---|---|---| | `equal?` | Is this the **same object**? | identity | never | | `==` | Do these have the **same value**? | identity | yes, by most value classes | | `eql?` | Are these the **same hash key**? | identity | yes, together with `hash` | The default for all three is identity, so for a class you write without overriding anything, `a == b` is only true when `a` and `b` are the same object. `!=` is simply the negation of `==`. ## `equal?` and `object_id` `equal?` is **identity**. Every live object has an `object_id`, an Integer that stays the same for the object's lifetime and is never shared by two live objects, and `a.equal?(b)` is true exactly when they are the same object. The core documentation says `equal?` should never be overridden, because code relies on it to mean identity. A few kinds of objects are reused by the interpreter, so identity holds even between values you built separately: - `nil`, `true`, `false`, small integers and symbols are **immediate values**; `(21 * 2).equal?(42)` is true. - Two separately built strings are different objects: `"ruby".equal?("ruby".dup)` is false. ## `==`: value equality `==` is what most code calls, and classes override it to compare contents: - `String#==` compares characters. - `Array#==` compares elements pairwise with `==`. - `Integer#==` and `Float#==` compare **numeric value across types**, so `1 == 1.0` and `1 == Rational(1, 1)` are true. ## `eql?`: equality for hash keys `eql?` exists so that `Hash` (and `Array#uniq`, and `Set`) can decide whether two objects are the same **key**. The rule, stated in the core docs, is that whenever `a.eql?(b)` is true, `a.hash == b.hash` must also be true, so a class that overrides one overrides both. Most classes make `eql?` behave like `==`. Numbers are the famous exception: `Integer#eql?` returns true only if the other object is also an Integer with the same value. So: ```ruby 1 == 1.0 # => true (numeric comparison across types) 1.eql?(1.0) # => false (different classes) 1.equal?(1) # => true (small integers are immediate) prices = {1 => :one} prices[1.0] # => nil [1, 1.0].uniq # => [1, 1.0] ``` The same split shows up in value classes built with `Data.define` or `Struct`: their `==` compares members with `==`, while their `eql?` compares members with `eql?`, so a member of `1` versus `1.0` makes them `==` but not `eql?`. ## Quick checks that trip people up - `[1, 2] == [1.0, 2.0]` is **true**, because `Array#==` compares elements with `==`; `[1, 2].eql?([1.0, 2.0])` is **false**, because `Array#eql?` compares them with `eql?`. - `"a" == :a` is **false**: a String and a Symbol are never `==`, even with the same text. - `nil == false` is **false**: both are falsy, but they are different objects of different classes. - `1.0.eql?(1.0)` is **true**: same class, same value. Each of these follows from one rule: `==` is whatever the receiver's class defines as "same value", and `eql?` adds "and usable as the same hash key". ## Putting it together 1. Use `==` in ordinary code; it is the method classes intend you to call. 2. Use `equal?` only when you really need "the same object", for example to detect that a method received the very object it already holds. 3. Remember that hash-based collections use `eql?` and `hash`, so a lookup can miss even though `==` says the values match. 4. When you write your own value class, override `==`, `eql?` and `hash` together. Two other methods are easy to confuse with these but answer different questions: `===` is case equality used by `case`/`when`, and `<=>` is ordering, from which `Comparable` derives its own `==`. ## Why interviewers ask it It is the Ruby version of the classic identity-versus-equality screen, with a twist that separates candidates: the third method, `eql?`, and the fact that numbers make it stricter than `==`. A candidate who connects `eql?` to `Hash` lookups and explains `{1 => :one}[1.0]` returning `nil` has understood why the three exist.
- What do ==, eql? and equal? do on an instance of a class you wrote without overriding anything?All three compare identity. `==` and `equal?` come from `BasicObject` and `eql?` from `Kernel`, and each returns true only when both sides are the same object. Two `Point.new(1, 2)` objects are therefore not `==`, and a `Hash` treats them as different keys until the class overrides `==`, `eql?` and `hash`.
- Why can't you rely on object_id to compare two strings with the same text?`object_id` identifies an object, not its contents. Two strings built separately, such as `"ruby"` and `"ruby".dup`, are distinct objects with distinct ids, even though they are `==`. Only immediate values such as small integers, symbols and `nil`, or objects the interpreter deliberately reuses, share an id.
saying these in an interview costs you the question
- equal? compares values and == compares object identity.
- eql? is just an alias of == for every class, including numbers.
- Because 1 == 1.0, the key 1.0 finds the entry stored under 1 in a Hash.
- Overriding equal? is the normal way to define equality for a value class.
- Two strings with the same text always share an object_id.