Binding Values in Patterns
The @ (at) binding operator (variable @ subpattern) lets a pattern test a value against a subpattern (such as a range, literal alternative, or nested destructuring pattern) while simultaneously binding the matched value to variable.
Why @ Bindings Are Needed
When you match against a range (100..=199) or an or-pattern (200 | 201 | 204) without @, the pattern verifies that the value falls in the range, but you have no variable name to print or pass the exact matched value inside the arm body:
| Pattern Form | Tests Against Subpattern? | Binds Matched Value to Variable? | Destructures Inner Fields? |
|---|---|---|---|
100..=199 |
Yes | No | N/A |
code |
Matches anything | Yes (code) |
No |
code @ 100..=199 |
Yes | Yes (code) |
N/A |
pkt @ Packet { id: 1..=10, .. } |
Yes (id in 1..=10) |
Yes (pkt = whole Packet) |
Also allows binding inner fields! |
Binding Range and Or-Pattern Matches with @
enum HttpStatus {
Code(u16),
Disconnected,
}
fn log_status(status: HttpStatus) {
match status {
HttpStatus::Code(code @ (200 | 201 | 204)) => {
println!("Success status {code}");
}
HttpStatus::Code(code @ 400..=499) => {
println!("Client error {code}: check request payload");
}
HttpStatus::Code(code @ 500..=599) => {
println!("Server error {code}: retry with backoff");
}
HttpStatus::Code(other) => {
println!("Other status {other}");
}
HttpStatus::Disconnected => {
println!("Connection closed before status received");
}
}
}
fn main() {
log_status(HttpStatus::Code(204));
log_status(HttpStatus::Code(404));
log_status(HttpStatus::Code(502));
}
Success status 204
Client error 404: check request payload
Server error 502: retry with backoff
Binding Both the Whole Struct and Its Destructured Fields
For Copy types (or when matching by reference so no conflicting move + borrow occurs), you can bind the outer value with whole @ ... while also binding or testing inner fields inside the subpattern:
#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn inspect_point(pt: Point) {
match pt {
// Bind the entire `Point` to `origin_axis` AND extract `x` and `y`:
origin_axis @ Point { x: 0, y } | origin_axis @ Point { x: y, y: 0 } => {
println!("On axis: {origin_axis:?} (nonzero coordinate = {y})");
}
whole @ Point {
x: 1..=10,
y: 1..=10,
} => {
println!("Inside 10x10 quadrant: {whole:?}");
}
other => println!("Outside target region: {other:?}"),
}
}
fn main() {
inspect_point(Point { x: 0, y: 7 });
inspect_point(Point { x: 4, y: 8 });
inspect_point(Point { x: -3, y: 12 });
}
On axis: Point { x: 0, y: 7 } (nonzero coordinate = 7)
Inside 10x10 quadrant: Point { x: 4, y: 8 }
Outside target region: Point { x: -3, y: 12 }
Using @ .. in Slice Patterns
Inside a slice pattern ([first, middle @ .., last]), attaching name @ to the rest pattern .. binds the matched middle subslice (&[T] or &mut [T]) to name:
fn main() {
let bytes: &[u8] = &[0xAA, 10, 20, 30, 0xFF];
if let [header, body @ .., checksum] = bytes {
println!("header: 0x{header:02X}, body: {body:?}, checksum: 0x{checksum:02X}");
}
}
header: 0xAA, body: [10, 20, 30], checksum: 0xFF