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

Rust Macros: From Preprocessor to Metaprogramming / Rust 宏:从预处理器到元编程

What you’ll learn / 你将学到: How Rust macros work, when to use them instead of functions or generics, and how they replace the C/C++ preprocessor. By the end of this chapter you can write your own macro_rules! macros and understand what #[derive(Debug)] does under the hood.

Rust 宏的工作原理、何时使用它们而非函数或泛型,以及它们如何替代 C/C++ 预处理器。在本章结束时,你将能编写自己的 macro_rules! 宏,并理解 #[derive(Debug)] 底层的运作机制。

Macros are one of the first things you encounter in Rust (println!("hello") on line one) but one of the last things most courses explain. This chapter fixes that.

宏是你最早接触到的 Rust 特性之一(第一行代码通常就是 println!("hello")),但却是大多数课程最后才解释的内容。本章将填补这一空白。


Why Macros Exist / 为什么需要宏

Functions and generics handle most code reuse in Rust. Macros fill the gaps where the type system can’t reach:

在 Rust 中,函数和泛型处理了大部分代码复用。宏则填补了类型系统无法触及的空白:

Need / 需求Function/Generic? / 函数/泛型?Macro? / 宏?Why / 原因
Compute value / 计算值fn max<T: Ord>(...)类型系统足以处理
Variadic args / 变长参数❌ Rust 无变长参数函数println!宏可以接收任意数量的 Token
Repetitive impl blocks❌ 仅靠泛型无法实现macro_rules!宏在编译时生成代码
Compile-time code executionconst fn 限制较多✅ Procedural macros完整的 Rust 代码可在编译时运行

Declarative Macros with macro_rules! / 使用 macro_rules! 的声明式宏

Declarative macros (also called “macros by example”) use pattern matching on syntax, similar to match on values.

声明式宏(也称为“示例宏”)在语法上使用模式匹配,类似于对值进行的 match 操作。

Basic syntax / 基础语法

macro_rules! say_hello {
    () => {
        println!("Hello!");
    };
}

fn main() {
    say_hello!();  // Expands / 展开为:println!("Hello!");
}

Fragment specifiers / 片段指示符参考表

Specifier / 指示符Matches / 匹配项Example / 示例
$x:expr任意表达式42, a + b, foo()
$x:ty类型i32, Vec<String>, &str
$x:ident标识符foo, my_var, MyStruct
$x:path路径std::collections::HashMap
$x:tt单个 Token 树任意内容 —— 通配符

Repetition / 重复 —— 宏的核心杀手锏

C/C++ macros can’t loop. Rust macros can repeat patterns:

C/C++ 宏无法循环。Rust 宏则可以重复模式:

#![allow(unused)]
fn main() {
macro_rules! make_vec {
    ( $( $element:expr ),* ) => {
        {
            let mut v = Vec::new();
            $( v.push($element); )*  // Repeat for each matched element
            v
        }
    };
}
}
Operator / 操作符Meaning / 含义Example / 示例
$( ... )*零个或多个vec![], vec![1, 2]
$( ... )+一个或多个至少需要一个元素
$( ... )?零个或一个可选元素

Common Standard Library Macros / 常用标准库宏

Macro / 宏What it does / 作用Expands to / 展开为
println!Format and print to stdoutstd::io::_print(...)
format!Format into a StringAllocates and returns a String
vec!Create a Vec with elementsVec::from(...)
todo!Mark unfinished codepanic!("not yet implemented")
assert!Panic if condition is falseif !cond { panic!(...) }
dbg!Print expr + value, return valueDebug-only inspection

dbg! — The debugging champion / dbg! —— 调试王者

dbg! returns the value it wraps, so you can insert it anywhere without changing program behavior. It prints to stderr with file:line info.

dbg! 会返回它包裹的值,且带有文件名和行号。它打印到 stderr,因此不会干扰 stdout。


Derive Macros / 派生宏

#[derive(Debug)] is a derive macro — it generates trait implementations automatically.

#[derive(Debug)] 是一个派生宏 —— 它会自动为结构体或枚举生成 Trait 实现代码。

Derive / 派生What it generates / 生成内容When to use / 场景
Debug{:?} 格式化几乎所有场景 —— 启用调试打印
Clone.clone() 方法需要复制值时
PartialEq==!= 运算符需要相等性比较时
DefaultType::default()具有合理的“零值”或空值时

Attribute Macros / 属性宏

Attribute macros transform the item they’re attached to.

属性宏会转换它们所附加的项。它们是语言语法的一部分,替代了 #pragma 和编译器扩展。

Attribute / 属性Purpose / 用途
#[test]标记为测试函数
#[cfg(...)]条件编译(替代 #ifdef
#[must_use]警告未使用的返回值
#[repr(C)]C 兼容的内存布局 (用于 FFI)

Exercises / 练习

🟢 Exercise 1: min! macro / 练习 1:min!

Write a min! macro that returns the smaller of two or three values.

#![allow(unused)]
fn main() {
macro_rules! min {
    ($a:expr, $b:expr) => { if $a < $b { $a } else { $b } };
    ($a:expr, $b:expr, $c:expr) => { min!(min!($a, $b), $c) };
}
}

🟡 Exercise 2: hashmap! / 练习 2:hashmap!

Create a hashmap! macro that supports key => value syntax and trailing commas.

#![allow(unused)]
fn main() {
macro_rules! hashmap {
    ( $( $key:expr => $value:expr ),* $(,)? ) => {
        {
            let mut map = std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    };
}
}