Declarative Macros (macro_rules!)
Use case
You want to generate repetitive code or a small DSL without writing a full procedural macro. Declarative macros use macro_rules! and pattern matching on the macro input to expand to code. They are hygienic and expand at compile time.
In C#: Like source generators or T4 templates; there is no direct equivalent to macro_rules!. Attributes and reflection are different. In Rust, macros are expanded before type checking.
Defining a macro
Use macro_rules! name { (pattern) => { expansion }; }. The pattern matches token trees; you can use $arg:expr (expression), $arg:ident (identifier), $arg:ty (type), etc. In the expansion, use $arg to insert the captured value.
macro_rules! twice {
($e:expr) => { { $e + $e } };
}
let x = twice!(2);
// expands to: let x = { 2 + 2 };In C#: No direct analogue; you'd write a method or use code generation.
Repetition
Use $( ... )* or $( ... ),* to repeat. For example, a macro that builds a vector from a list of expressions:
macro_rules! vec_of {
($($e:expr),*) => {
vec![ $($e),* ]
};
}
let v = vec_of!(1, 2, 3);In C#: Like params or collection initializers; in Rust the macro generates the literal list.
Common fragment specifiers
expr– expressionident– identifier (variable/type name)ty– typeblock– block{ ... }stmt– statementpat– pattern
In C#: N/A; macros operate on syntax.
Key takeaway
Use macro_rules! name { (pattern) => { expansion }; }. Use $x:expr etc. in the pattern and $x in the expansion. Use $( ... ),* for repetition. Macros are hygiene-aware; identifiers from the macro and from the call site don't accidentally collide.