In Ruby, how do the one-line expr in pattern and expr => pattern forms differ, and when is NoMatchingPatternKeyError raised instead of NoMatchingPatternError?
answer
- in answers yes or no
- => asserts and destructures
- single pattern, missing hash key
- #key and #matchee
- no guard on one-liners
basics
~20 sexpr in pattern returns true or false and never raises on a mismatch; expr => pattern binds and raises. A single-pattern match that fails on a missing hash key raises NoMatchingPatternKeyError, a NoMatchingPatternError subclass with key and matchee.
solid answer
~50 s`expr in pattern` is documented as the same as `case expr; in pattern; true; else false; end`: a boolean test, handy in `if` and in blocks like `events.select { |e| e in {type: "message"} }`. `expr => pattern` is a destructuring assertion: it binds the pattern's variables and raises `NoMatchingPatternError` if the value does not fit; it returns `nil`. When a single-pattern match — `=>`, or a `case/in` with exactly one `in` and no `else` — fails because a hash pattern's key is absent, the error is the subclass `NoMatchingPatternKeyError`, whose message names the key (`key not found: :type`) and which exposes `#key` and `#matchee`. A `case/in` with several branches raises plain `NoMatchingPatternError`. Rescuing `NoMatchingPatternError` catches both. Neither one-line form accepts a guard: `x in [a, b] if cond` is a modifier-`if` on the whole statement.
code
ruby · 22 linesevents = [
{type: "message", text: "hi"},
{type: "reaction_added", reaction: "tada"},
{text: "no type"}
]
p events.count { |e| e in {type: "message"} } # => 1
begin
events.last => {type:}
rescue NoMatchingPatternKeyError => e
p e.key # => :type
p e.matchee # => {text: "no type"}
end
begin
{type: 1} => {type: String}
rescue NoMatchingPatternKeyError
p :key_missing
rescue NoMatchingPatternError
p :shape_wrong # key present, value wrong
endgo deeper
Know that expr in pattern returns true or false while expr => pattern raises on mismatch and binds variables.
Explain when NoMatchingPatternKeyError appears, what key and matchee return, and why rescue order matters; note that one-line forms take no guard.
Use => as a validation step at a webhook boundary, map key and shape errors to precise rejections, and avoid reading variables bound by a failed in test.
Decide where assertion-style destructuring belongs versus explicit schema validation, balancing concise handlers against the quality of error reports sent back to integrators.
## Two one-line forms Ruby has two standalone pattern-matching operators besides `case/in`: | Form | Returns | On mismatch | Typical use | |---|---|---|---| | `expr in pattern` | `true` / `false` | returns `false` | conditions, filters, `any?`/`select` blocks | | `expr => pattern` | `nil` | raises | destructuring data whose shape you already expect | The language reference defines the first exactly: `expr in pattern` "is the same as `case expr; in pattern; true; else false; end`". And it describes `=>` as most useful "when the expected data structure is known beforehand, to just unpack parts of it" — it "will raise if the config's structure is unexpected". ```ruby event = {type: "message", user: {id: "U42"}, text: "hi"} if event in {type: "message", text: String} # boolean test, no exception possible from a mismatch end event => {user: {id: user_id}, text:} user_id # => "U42" text # => "hi" ``` Both forms **bind** the pattern's variables. With `in`, the binding is only meaningful when it returned `true`; the reference leaves variables from a failed match undefined. ## Which error, and when `NoMatchingPatternError` (a `StandardError`) is the base class. Its subclass **`NoMatchingPatternKeyError`** carries extra detail, but only in one situation. CRuby's compiler tracks a "key error" flag only for **single-pattern** matches — the `=>` operator, or a `case/in` with exactly one `in` branch and no `else` — and sets it when a hash pattern fails because a key is **missing**: ```ruby {text: "hi"} => {type:} # NoMatchingPatternKeyError: {text: "hi"}: key not found: :type ``` The exception exposes: - `#key` — the missing key (`:type`); - `#matchee` — the hash that lacked it. Other failures raise plain `NoMatchingPatternError`, whose message includes the value and, in single-pattern form, why it failed: 1. a key is present but its value does not match (`{type: 1} => {type: String}`); 2. an array pattern has the wrong length; 3. the value is not a Hash or Array and does not respond to `deconstruct_keys`/`deconstruct`; 4. a `case/in` with **several** branches finds no match — no single failure reason exists, so no key detail is reported. Because the key error is a subclass, `rescue NoMatchingPatternError` catches both; rescue the subclass first if you want to report the missing field. ## Validating a webhook with => `=>` gives a compact "parse, don't validate" step at the edge of a handler: ```ruby def handle(event) event => {type: "message", channel: String => channel, text: String => text} post(channel, text) rescue NoMatchingPatternKeyError => e reject("missing field #{e.key}") rescue NoMatchingPatternError reject("malformed event") end ``` Note the `rescue` order: the subclass must come first, otherwise the general clause wins. ## No guards on the one-liners `case/in` branches accept `if`/`unless` guards. The one-line forms do not; the reference warns that `[1, 2] in a, b if b == a*2` "is parsed as a standalone expression with modifier `if`". Write the extra condition separately: ```ruby ok = (event in {ts: Integer => ts}) && ts > cutoff ``` ## Choosing between them - Use **`in`** to ask a question: filters (`events.count { |e| e in {type: "reaction_added"} }`), `if`/`unless` conditions, feature detection. - Use **`=>`** to assert and unpack when a mismatch is a bug or invalid input you intend to reject. - Use **`case/in`** when there are several shapes to handle. ## Version notes One-line matching arrived experimentally in 3.0, when `in` was changed to return a boolean; both forms have been stable since 3.1, which also allowed the parentheses around the pattern to be omitted. Ruby 3.4 changed `Hash#inspect`, so messages in 4.0 print hashes as `{text: "hi"}`.
- Why does a multi-branch case/in raise plain NoMatchingPatternError even when a key is missing?CRuby only records the missing-key detail for single-pattern matches: `=>` or a `case/in` with one `in` and no `else`. With several branches there is no single reason for the failure, since each branch may have failed differently, so Ruby raises the base `NoMatchingPatternError`.
- Can you add an if guard to `event in {ts: Integer => ts}`?No. Guards exist only on `case/in` branches. A trailing `if` after a one-line match is parsed as a modifier on the whole statement, as the reference warns. Combine the test explicitly instead: `(event in {ts: Integer => ts}) && ts > cutoff`.
saying these in an interview costs you the question
- expr in pattern raises NoMatchingPatternError when the pattern does not match.
- expr => pattern returns true or false like the in operator.
- NoMatchingPatternKeyError is a subclass of KeyError, so rescue KeyError catches it.
- Every failed case/in reports the missing key through NoMatchingPatternKeyError.
- A one-line in accepts an if guard exactly like a case/in branch.