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?
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
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
Fun fact, ===
is also used to match exceptions in rescue
Here is an example
class Example
def self.===(exception)
puts \"Triple equals has been called.\"
true
end
end
raise rescue Example
# => prints \"Triple equals has been called.\"
# => no exception raised
This is used to match system errors.
SystemCallError.===
has been defined to return true when the two have the same errno. With this system call errors with the same error number, such as Errno::EAGAIN
and Errno::EWOULDBLOCK
, can both be rescued by listing just one of them.