Case Expressions
Ruby's case expression is a more powerful cousin of the switch statement found in C-family languages. Instead of comparing values strictly by equality, each when clause is evaluated using the === (triple-equals) operator, called on the when value with the case subject as its argument. Because different classes implement === differently — Range checks membership, Regexp checks for a match, Class checks is_a?, and Proc/Lambda invokes the callable — a single case expression can pattern-match against wildly different criteria without any special syntax. case is also an expression, not just a statement, so it returns a value that can be assigned directly.
Cricket analogy: Ruby's case is like a third umpire reviewing a dismissal against several different possible criteria at once -- a run-out check, an lbw check, a catch check -- each using its own specific evaluation method, unlike a simple switch that would only compare one fixed value.
The role of ===
The key to understanding case/when is that when value desugars to value === subject, not subject == value. This ordering matters: it's the when clause's class that decides how the comparison happens. Integer === 5 checks whether 5 is an Integer; (1..10) === 5 checks range membership; /^b/ === 'bar' checks whether the string matches the regex. This is why case can elegantly branch on type, range, and pattern all in the same expression.
Cricket analogy: when value desugars to value === subject, so it's the when clause deciding the comparison, like how a specific fielding-restriction rule (the powerplay range) decides whether over 5 counts, rather than over 5 deciding which rule applies to it.
def describe(value)
case value
when Integer
"an integer"
when String
"a string"
when /^admin_/
"an admin-prefixed string"
when nil
"nothing at all"
else
"something else entirely"
end
end
puts describe(42) # "an integer"
puts describe("hello") # "a string"Subject-less case, and structural pattern matching with case/in
You can omit the subject entirely and use case as a cleaner alternative to a long if/elsif chain — each when clause then acts as its own boolean condition. Separately, since Ruby 2.7, case/in provides structural pattern matching, distinct from ===-based case/when: it can destructure arrays and hashes, bind variables from their contents, and apply guard clauses, which is especially useful when working with nested data such as parsed JSON.
Cricket analogy: Omitting the case subject is like a captain running through field settings as a checklist of true/false conditions instead of comparing against one ball's trajectory; case/in, meanwhile, is like the analyst destructuring a full match scorecard JSON into named fields (runs, wickets, overs) with guard conditions on each.
# subject-less case, reads like a cleaner if/elsif chain
score = 82
grade = case
when score >= 90 then "A"
when score >= 80 then "B"
else "F"
end
# case/in destructures a hash and binds a variable
config = { role: "admin", permissions: ["read", "write"] }
case config
in { role: "admin", permissions: }
puts "Admin with permissions: #{permissions.join(', ')}"
in { role: "guest" }
puts "Guest access"
endPython only gained a structural match statement in 3.10 (2021), decades after Ruby's case/when shipped ===-based matching. JavaScript still has no true pattern-matching switch — its switch uses strict === equality only, so it can't branch on ranges, regexes, or classes the way Ruby's case can.
Order matters in case/when: Ruby evaluates when clauses top to bottom and stops at the first match. Placing a broad matcher like when Integer before a narrower one like when 1..9 means the narrower clause becomes unreachable dead code — always order when clauses from most specific to least specific.
case/whencompares usingwhen_value === subject, not==, which is why thewhenclause's type determines matching behavior.- Range, Regexp, Class/Module, and Proc all implement
===differently, enabling flexible matching without special syntax. casewithout a subject works like a cleanerif/elsifchain, evaluating eachwhenas its own boolean expression.caseis an expression and returns a value, so its result can be assigned directly to a variable.case/in(Ruby 2.7+) is a distinct structural pattern-matching feature that can destructure arrays/hashes and bind variables.whenclauses are checked in order, so broader matchers placed before narrower ones will shadow the more specific cases.
Practice what you learned
1. In `case subject; when value; end`, what comparison is actually performed?
2. Why can `case` match against a Range in a `when` clause?
3. What feature was introduced in Ruby 2.7 to allow destructuring hashes and arrays inside a case expression?
4. What does a subject-less `case` expression (just `case` followed by `when` boolean conditions) behave like?
5. Given `case 15; when Integer; puts 'int'; when 10..20; puts 'range'; end`, what is printed?
Was this page helpful?
You May Also Like
Conditional Statements in Ruby
How to control program flow in Ruby with if/elsif/else, unless, ternary expressions, and modifier conditionals, plus Ruby's truthiness rules.
Operators in Ruby
A tour of Ruby's arithmetic, comparison, logical, assignment, and range operators, including the fact that most operators are actually method calls.
Duck Typing in Ruby
Explore Ruby's duck typing philosophy — objects are defined by the methods they respond to, not by their class — and how it shapes idiomatic, flexible Ruby code.
Loops in Ruby
A tour of Ruby's repetition constructs — while, until, for, loop, and the more idiomatic times/each iterators — with guidance on which one to reach for.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics