=== vs. == in Ruby
In Ruby, what is the difference between == and ===? The RDoc says
Case Equality—For class Object, effectively the same as calling #==, but typically overridden by descendents to provide meaningful semantics in case statements.
Is #==
the same as ==
? And could you provide an example of when/how this is used in case statements?
Solution 1:
The two really have nothing to do with each other. In particular, #==
is the equality operator and #===
has absolutely nothing to with equality. Personally, I find it rather unfortunate that #===
looks so similar to #==
, uses the equals sign and is often called the case equality operator, triple equals operator or threequals operator when it really has nothing to do with equality.
I call #===
the case subsumption operator (it's the best I could come up with, I'm open to suggestions, especially from native English speakers).
The best way to describe a === b
is "if I have a drawer labeled a
, does it make sense to put b
in it?"
So, for example, Module#===
tests whether b.is_a?(a)
. If you have Integer === 2
, does it make sense to put 2
in a box labeled Integer
? Yes, it does. What about Integer === 'hello'
? Obviously not.
Another example is Regexp#===
. It tests for a match. Does it make sense to put 'hello'
in a box labeled /el+/
? Yes, it does.
For collections such as ranges, Range#===
is defined as a membership test: it makes sense to put an element in a box labeled with a collection if that element is in the collection.
So, that's what #===
does: it tests whether the argument can be subsumed under the receiver.
What does that have to with case
expressions? Simple:
case foo
when bar
baz
end
is the same as
if bar === foo
baz
end
Solution 2:
Yes, by #==
the docs mean "the instance method ==
of the current object".
===
is used in case statements as such:
case obj
when x
foo
when y
bar
end
Is the same as
if x === obj
foo
elsif y === obj
bar
end
Some classes that define their own ===
are Range (to act like include?
), Class (to act like obj.is_a?(klass)
) and Regexp
(to act like =~
except returning a boolean). Some classes that don't define their own ===
are the numeric classes and String.
So
case x
when 0
puts "Lots"
when Numeric
puts(100.0 / x)
when /^\d+$/
puts(100.0 / x.to_f)
default
raise ArgumentError, "x is not a number or numeric string"
end
is the same as
if 0 == x
puts "Lots"
elsif x.is_a? Numeric
puts(100.0 / x)
elsif x =~ /^\d+$/
puts(100.0 / x.to_f)
else
raise ArgumentError, "x is not a number or numeric string"
end