Alec Wenzowski
Alec Wenzowski

Reputation: 3908

Why does a switch statement perform equality testing differently from an if statement?

Why does the if statement work in the example below while the switch statement does not.

In the trivial example above, the switch statement is overkill, but there are obviously situations where a switch statement would be DRYer than chained elsifs

Upvotes: 3

Views: 380

Answers (4)

ian
ian

Reputation: 12251

The quick and simple answer is that case uses === (3 equals) and not two.

$ irb                                                                 
if ''.class == String
  puts "yep, that's a string"   
end 

yep, that's a string

=> nil

if ''.class === String
  puts "yep, that's a string"
end
=> nil

Upvotes: 5

Marnen Laibow-Koser
Marnen Laibow-Koser

Reputation: 6337

As others have said, case equality in Ruby works a bit differently than you might expect, so you can just do

case foo
when String # that is, when String === foo, more or less when foo.class == String
  do something
end

But generally, you shouldn't. If you're explicitly testing class names, then (usually) your OO design is flawed -- in most cases, you should try to use polymorphism instead. In other words, instead of

if x.class == String
  x.process_string
else
  x.process_non_string
end

you should simply have x.process, and then define process differently for String and other classes. Cleaner, less code, doesn't force the caller to know the class of the called object.

Upvotes: 3

AboutRuby
AboutRuby

Reputation: 8116

This is because the case statement doesn't use the == operator, it uses the === operator (sometimes called the case equality operator). What this does varies depending on what's on the left side of the operator. So, if you were to transform case statement like this:

case "Some string"
when String
  puts "It's a string!"
else
  puts "It's not a string!"
end

Into an if statement, it would become this:

if String === "Some string"
  puts "It's a string!"
else
  puts "It's not a string!"
end

Note that Ruby does this backwards from how you'd expect, it does String === "Some string". This is because what you really want to do is call Class#=== here, and not String#===. What the === operator does for any object is really up to the class. In the case of Class#===, it's roughly equivalent to calling "Some string".is_a?(String). But if you were to do "a" === "b", the String#=== method is roughly equivalent to String#==.

It can get confusing, but the operator's usage is largely idiomatic. In other words, the "class object in a when statement" idiom means to test if the case object is of that class. I've written an article on this that explains it a bit more, you can read it here.

Upvotes: 5

Mathieu Mahé
Mathieu Mahé

Reputation: 2746

Actually, ruby's "case" makes the comparaison with ===

So your example is equivalent to :

if ''.class === String
   puts "yep, that's a string"
end

Upvotes: 11

Related Questions