Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Crate structure

A procedural macro must live in its own crate, separate from the code that uses it.

Why a separate crate?

Procedural macros run at compile time. The compiler doesn’t just read your macro — it runs it. To make that possible, a procedural macro must be compiled into an executable that the compiler can invoke while compiling any crate that uses the macro. That executable has to exist before those crates are built, so the macro needs its own compilation unit — a separate, distinct crate.

Creating a proc-macro crate

A proc-macro crate looks like a regular library crate with one key difference in Cargo.toml:

[package]
name = "my-macros"
version = "0.1.0"
edition = "2024"

[lib]
proc-macro = true

The proc-macro = true flag tells the compiler that this crate exports procedural macros. Without it, attempting to use #[proc_macro_derive] or any other proc-macro attribute will produce a compilation error.

The proc_macro crate

When you set proc-macro = true, your crate automatically gets access to the proc_macro crate from the standard library. You don’t need to add it to [dependencies] — it’s provided by the compiler.

This crate gives you the core type you’ll work with: TokenStream. Every procedural macro is a function that takes a TokenStream as input and returns a TokenStream as output.

#![allow(unused)]
fn main() {
use proc_macro::TokenStream;

#[proc_macro_derive(MyMacro)]
pub fn my_macro(_input: TokenStream) -> TokenStream {
    // Return nothing — the derive macro adds no extra code for now.
    TokenStream::new()
}
}

Wiring it up

To use your proc-macro crate from another crate, add it as a dependency:

[dependencies]
my-macros = { path = "../my-macros" }

Then you can use the derive macro:

#![allow(unused)]
fn main() {
use my_macros::MyMacro;

#[derive(MyMacro)]
struct Foo;
}

Workspace layout

In a workspace, proc-macro crates are just regular members. This workshop uses a workspace with exercises as members, and some exercises will include their own proc-macro crate as a path dependency.

Exercise

The exercise for this section is located in 03_basics/01_crate_structure

Create a proc-macro crate from scratch and wire it up. You’ll need to create the Cargo.toml (with proc-macro = true), write a derive macro that returns an empty token stream, and add it as a dependency so the provided code compiles.