GGistDev

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 (via cargo install cargo-expand) shows expanded code
  • Add #[macro_export] to expose from a crate root; prefer pub use re-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 separate proc-macro crate that manipulates TokenStream.

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