Macros in Rust
Macros generate code at compile time. Use macro_rules! for declarative macros and procedural macros for derives/attributes/functions.
macro_rules! basics
macro_rules! my_vec {
( $( $x:expr ),* $(,)? ) => {{
let mut v = Vec::new();
$( v.push($x); )*
v
}};
}
let v = my_vec![1, 2, 3];
- Matchers:
expr,ident,tt, etc. - Repetition:
$( ... )*or+with separators.
Hygiene and scopes
Macros are hygienic: introduced identifiers do not capture or get captured unintentionally. Use $crate for robust crate‑relative paths.
Debugging macros
cargo expand(viacargo install cargo-expand) shows expanded code- Add
#[macro_export]to expose from a crate root; preferpub usere-exports to control API paths
Procedural macros (overview)
- Derive macros:
#[derive(Serialize, Deserialize)] - Attribute macros:
#[route(GET, "/")] - Function‑like macros:
my_macro!(...)Defined in a separateproc-macrocrate that manipulatesTokenStream.
Tips
- Prefer functions over macros when possible for type checking and readability
- Keep
macro_rules!small and well‑documented - Use proc‑macros for boilerplate derives and DSLs when functions/generics can’t express the pattern