# Rust Macros

> When to activate: Rust macros, macro_rules!, procedural macros, derive macros, attribute macros, function-like macros, syn, quote

- Skill: `mattakushi432/rust-macros` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/rust-macros`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/rust-macros/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/rust-macros

---


# Rust Macros

## Declarative Macros (macro_rules!)

```rust
macro_rules! vec_of_strings {
    ($($x:expr),* $(,)?) => {
        vec![$($x.to_string()),*]
    };
}

let names = vec_of_strings!["alice", "bob", "charlie"];

// Multiple arms
macro_rules! assert_approx_eq {
    ($a:expr, $b:expr) => { assert_approx_eq!($a, $b, 1e-6) };
    ($a:expr, $b:expr, $tol:expr) => {
        let diff = ($a - $b).abs();
        assert!(diff < $tol, "assertion failed: |{} - {}| = {} >= {}", $a, $b, diff, $tol);
    };
}

// impl From for multiple types
macro_rules! impl_from {
    ($from:ty => $to:ty, $variant:ident) => {
        impl From<$from> for $to {
            fn from(e: $from) -> Self { <$to>::$variant(e) }
        }
    };
}

impl_from!(std::io::Error => AppError, Io);
impl_from!(serde_json::Error => AppError, Json);
```

## Procedural Macros: Custom Derive

```toml
# my-derive/Cargo.toml
[lib]
proc-macro = true

[dependencies]
syn = { version = "2", features = ["full"] }
quote = "1"
proc-macro2 = "1"
```

```rust
// my-derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(Describe)]
pub fn describe_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let name_str = name.to_string();

    TokenStream::from(quote! {
        impl Describe for #name {
            fn describe(&self) -> &'static str { #name_str }
        }
    })
}
```

```rust
// Usage
use my_derive::Describe;

trait Describe { fn describe(&self) -> &'static str; }

#[derive(Describe)]
struct User { name: String }

let u = User { name: "alice".into() };
println!("{}", u.describe()); // "User"
```

## Attribute Macros

```rust
#[proc_macro_attribute]
pub fn retry(attr: TokenStream, item: TokenStream) -> TokenStream {
    let attempts: usize = parse_retry_attr(attr);
    let item_fn = parse_macro_input!(item as syn::ItemFn);
    let fn_sig = &item_fn.sig;
    let fn_body = &item_fn.block;

    TokenStream::from(quote! {
        #fn_sig {
            let mut remaining = #attempts;
            loop {
                let result = (|| #fn_body)();
                match result {
                    Ok(v) => return Ok(v),
                    Err(e) if remaining > 1 => { remaining -= 1; }
                    Err(e) => return Err(e),
                }
            }
        }
    })
}
```

## Standard Compile-Time Macros

```rust
// Include files at compile time
let config = include_str!("../config/default.toml");
let icon = include_bytes!("../assets/icon.png");

// Environment variables at compile time
let version = env!("CARGO_PKG_VERSION");
let opt_debug = option_env!("DEBUG_MODE");

// Conditional compilation
#[cfg(target_os = "macos")]
fn platform_init() { /* macOS specific */ }

#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
fn optimized_path() { /* 64-bit only */ }

// Derive multiple standard traits
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
struct UserId(u64);
```

## Common Anti-Patterns

- **Macros instead of functions** — prefer functions; only use macros when repetition is inexpressible otherwise
- **Unhygienic macros** — use `$crate::` to refer to your own crate items
- **Proc macros without good error messages** — use `syn::Error::new_spanned` to point to the exact token
- **Recursive macros without a base case** — they will expand infinitely and hit the recursion limit
- **Forgetting `#[macro_export]`** — without it, the macro is private to the module

