case and when in depth

What when compares

case takes a value and asks each when whether it matches. The comparison is not ==; it is ===, and each class defines it for a useful kind of match. That is why a when can hold a range or a class and still read like a sentence.

ruby -e 'case 7; when 1..5 then puts :low; else puts :high; end'

high

7 does not fall in 1..5, so the else branch runs. then is optional; it lets the body sit on the same line as the when, which is what the one-liners use.

Ranges, classes, regexes, lambdas

Four kinds of match cover most real code. A class matches any instance of it:

ruby -e 'case "x"; when Integer then puts :int; when String then puts :str; end'

str

A regex matches a string that contains the pattern:

ruby -e 'case "error 500"; when /5\d\d/ then puts :server_error; else puts :ok; end'

server_error

A lambda works because Proc defines === as a call, so any predicate can be a when:

ruby -e 'even = ->(n) { n.even? }; case 4; when even then puts :even; else puts :odd; end'

even

Ranges, classes and regexes are all objects with a ===; a lambda simply calls its body. Once that is clear, a case with a dozen branches stays readable.

The missing else

A case without a matching when and without an else returns nil and carries on. It does not raise, which is how a value falls through a long list without a word:

ruby -e 'case 9; when 1 then puts :one; end; puts "after"'

after

When the list is meant to cover every case, write the else and raise there. A missing branch then becomes an error with the value in the message, instead of a nil that surfaces three methods later.

case in as a first taste

Ruby 3 adds a second form, case/in, for matching the shape of a value rather than its identity:

ruby -e 'case {status: "ok", code: 200}; in {status: "ok", code: code}; puts code; end'

200

The in pattern matches a hash with those keys and binds code to the value. Lesson 37, later in the course, covers the whole form; the point here is that case is the same keyword, with a stricter matcher.

What to watch for

Order matters: the first matching when wins, so a broad range before a narrow one hides the narrow case. Put the specific patterns first, or the general one last.

A case on a class name compares the class, not the value: when Integer matches any integer, and when 1 matches only one. Mixing the two in one list is fine as long as the order is right.

Next: Guard clauses and modifiers

Article author: Arthur Isaev