21.9 Binding Values While Testing: The @ Pattern

The @ (“at”) operator lets you bind a value to a variable while simultaneously testing it against a pattern.

fn check_error_code(code: u16) {
    match code {
        // Match codes 400-499, bind the matched code to `client_error_code`
        client_error_code @ 400..=499 => {
            println!("Client Error code: {}", client_error_code);
        }
        // Match codes 500-599, bind to `server_error_code`
        server_error_code @ 500..=599 => {
            println!("Server Error code: {}", server_error_code);
        }
        // Match any other code
        other_code => {
            println!("Other code: {}", other_code);
        }
    }
}

fn main() {
    check_error_code(404); // Output: Client Error code: 404
    check_error_code(503); // Output: Server Error code: 503
    check_error_code(200); // Output: Other code: 200
}

Here, client_error_code @ 400..=499 first checks if code is in the range. If yes, the value of code is bound to client_error_code for use within the arm. This is useful when you need the value that matched a specific condition (like a range or enum variant) within the corresponding code block.

It works well with simple values (integers, chars) and enum variants. Matching complex types like String against literals using @ requires care; often, a combination of binding and a match guard is more idiomatic:

fn check_message(opt_msg: Option<String>) {
    match opt_msg {
        // Bind the String to `msg`, then use a guard to check its value
        Some(ref msg) if msg == "CRITICAL" => {
            println!("Handling critical message!");
        }
        // Bind any Some(String) using `ref` to avoid moving the string
        Some(ref msg) => {
            println!("Received message: {}", msg);
        }
        None => {
             println!("No message.");
        }
    }
}
fn main() {
    check_message(Some("CRITICAL".to_string())); // Output: Handling critical message!
    check_message(Some("INFO".to_string()));    // Output: Received message: INFO
    check_message(None);                       // Output: No message.
}