delegate_match!() { /* proc-macro */ }Expand description
Convenience macro for writing grouped match arms for different underlying types.
Writing repetitive match arms for enumerations (or other pattern-matching
constructs) — especially when types, but not the API, differ —
can quickly become boilerplate. delegate_match! lets you list
several patterns up-front once and then re-uses a single body for each
of them, automatically expanding into equivalent ordinary Rust code.
§Syntax outline
match <scrutinee_expr> {
[<arm_path>::]{ <entry_pat> [: <assoc_ts>][, ...] } [<arm_pat>] [if <guard_expr>] => <body_expr>[[,] ...]
}arm_path— optional path prefix (e.g.MyEnumor::std::io)entry_pat— individual entry pattern, also available as the$entry_patplaceholder.assoc_ts— associated tokens, also available as the$assoc_tsplaceholder.arm_pat— an optional pattern appended to every entry.guard_expr— an optionalifguard.body_expr— expression generated for each entry.
This expands to:
match <scrutinee_expr> {
<arm_path>::<entry_pat><arm_pat> if <guard_expr> => <body_expr>
}Two placeholders are substituted for every entry before the code is type-checked, and they may appear in the following places:
- inside the delegate arm pattern
arm_pat(if present), - inside the match arm guard expression
guard_expr(if present), - inside the arm body expression
body_expr.
The available placeholders are:
$entry_pat— the entry pattern for a generated arm.$assoc_ts— the tokens following an entry, up until the next one (excluding the colon).
The macro is supposed to accept standard Rust match expression syntax, extended with the above.
Any other deviation should generally be considered a bug.
§Semantics
- For each entry in a grouped arm, the macro generates a regular match arm.
- Everything else (outer attributes, guards, commas…) is preserved exactly as you write it.
- The only exception to that is placeholder substitution.
- This macro performs generation before type-checking is done, so the generated code is capable of working with different types, if constructed appropriately.
- The order of generated arms is the order of entries in the source code.
§Examples
§Delegating to the same code for multiple enum variants
use delegate_match::delegate_match;
enum MouseEvent { Scroll(i16, i16), Position(i32, i32) }
let ev = MouseEvent::Scroll(10, 20);
delegate_match! {
match ev {
// This expands to two individual arms.
MouseEvent::{ Scroll, Position }(x, y) => {
println!("mouse event: $entry_pat → ({x}, {y})")
}
}
}§Using placeholders
delegate_match! {
match msg {
// `$assoc_ts` and `$entry_pat` are substituted at compile time.
Msg::{ Ping: "🏓", Log: "📝" } => {
// Outputs "🏓 Ping" or "📝 Log" depending on the variant.
println!("{} {}", $assoc_ts, stringify!($entry_pat))
}
}
}§Adding an if guard to multiple entries
delegate_match! {
match n {
// This works despite `val` being of different types for each variant.
// This is because a separate arm is generated for each entry!
Number::{ I32, I64 }(val) if val % 2 == 0 => {
println!("even {}", val)
}
// We must also account for the rest of the cases.
Number::{ I32, I64 }(val) => {
println!("odd {}", val)
}
}
}