Skip to content

Binding with @

A plain binding (n =>) captures any value without restriction. Ranges (1..=5) check without capturing. @ does both at once: it checks the sub-pattern and binds the value to a name.

Syntax

name @ sub-pattern => body

The sub-pattern can be a range, an or-pattern, a literal — any valid pattern. If the sub-pattern does not match, the arm is skipped normally.

@ with numeric ranges, or-pattern and score with label.

07-binding-with-at.zolo
Playground
// Feature: Binding with `@` — capture AND check a subpattern
// Syntax: `name @ subpattern => ...`
// When to use: you need the value that matched a structured pattern
// (range, literal, or-pattern) — instead of just checking it.

let n = 5
let msg = match n {
  x @ 1..=3 => "small: {x}",
  x @ 4..=6 => "medium: {x}",
  x @ 7..=9 => "large: {x}",
  x => "other: {x}",
}
print(msg)  // medium: 5

// Binding with or-pattern.
let day = "Saturday"
let label = match day {
  d @ ("Saturday" | "Sunday") => "weekend ({d})",
  d => "weekday ({d})",
}
print(label)  // weekend (Saturday)

// Binding with continuous numeric range.
let score = 92
let r = match score {
  s @ 90..=100 => "great ({s})",
  s @ 70..=89 => "good ({s})",
  s => "bad ({s})",
}
print(r)  // great (92)
// expected:
// medium: 5
// weekend (Saturday)
// great (92)

@ is especially useful when you need to use the value in a string interpolation or calculation inside the arm, without giving up structured checking. Compare:

  • x if x >= 1 && x <= 5 => ... — guard, works but more verbose.
  • x @ 1..=5 => ...@ with range, more declarative.

Challenge

Rewrite the third example in the file using guards instead of @ and compare readability. Which do you prefer for that case?

enespt-br