81

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?

1

3 Answers 3

147

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 
Sign up to request clarification or add additional context in comments.

12 Comments

Array#=== is not defined as membership in ruby 1.8 or 1.9.1. Range#=== is though.
@sepp2k: You're right. That's what I get for assuming sensible semantics without checking the documentation first.
"if I have a drawer labeled a, does it make sense to put b in it?". Splendid image.
What's the etymology of this operator? Was it used like this in Perl or CLU, or something made up new by Matz?
@BKSpurgeon: Really? I'm not even a native speaker, and I know that word. And I see it quite commonly used. Especially in programming. For example in the famous Jigsaw paper: "Mixin based inheritance subsumes other forms of linear multiple inheritance typical of LISP based object oriented languages" and from the same paper "Just as modules and interfaces subsume some functions supported by linkers, inheritance subsumes some functions of text editors".
|
12

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 

2 Comments

Curious if you were to put a string in the when statement, would it be similar to saying case x; when string --> if "string" == x?
@the12 Are you asking whether Ruby will automatically add quotes around an identifier or was that a typo? Anyway case x; when string is equivalent to if string === x, which, if string contains a string, is equivalent to if string == x. Likewise case x; when "string" is equivalent to if "string" === x and if "string" == x.
6

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.