🦀 第18课:宏系统

实用工具 第18/25课

Rust的宏系统远比C的预处理器宏强大。它是在AST层面操作的代码生成工具,分为声明宏(macro_rules!)和过程宏(proc macro)。本课深入宏的核心概念和实际应用。

📝 声明宏 macro_rules!

// 简单宏
macro_rules! say_hello {
    () => {
        println!("Hello from macro! 🦀");
    };
}

// 带参数的宏
macro_rules! create_function {
    ($func_name:ident) => {
        fn $func_name() {
            println!("函数 {} 被宏创建", stringify!($func_name));
        }
    };
}

create_function!(foo);
create_function!(bar);

// 可变参数
macro_rules! sum {
    ($($x:expr),*) => {{
        0 $(+ $x)*
    }};
}

// 多种匹配模式
macro_rules! command {
    (hello) => { println!("你好!") };
    (hello $name:expr) => { println!("你好, {}!", $name) };
    (add $a:expr, $b:expr) => { println!("{} + {} = {}", $a, $b, $a + $b) };
}

fn main() {
    say_hello!();
    foo();
    bar();
    
    println!("sum: {}", sum!(1, 2, 3, 4, 5));
    println!("sum: {}", sum!(10, 20));
    
    command!(hello);
    command!(hello "Rust");
    command!(add 3, 5);
}
Hello from macro! 🦀 函数 foo 被宏创建 函数 bar 被宏创建 sum: 15 sum: 30 你好! 你好, Rust! 3 + 5 = 8

✅ 验证通过

🔧 宏的匹配模式

// 片段分类符
// expr  → 表达式: 1 + 2, foo()
// ident → 标识符: x, my_func
// ty    → 类型: i32, String
// path  → 路径: std::collections::HashMap
// stmt  → 语句: let x = 5;
// block → 代码块: { ... }
// pat   → 模式: Some(x), _
// literal → 字面量: 42, "hello"
// meta  → 属性: #[derive(Debug)]
// item  → 项: fn foo() {}, struct Bar;
// vis   → 可见性: pub, pub(crate)
// tt    → token树: 任何token

macro_rules! hashmap {
    ($($key:expr => $value:expr),*) => {{
        let mut map = std::collections::HashMap::new();
        $(map.insert($key, $value);)*
        map
    }};
}

macro_rules! vec_of {
    ($element:expr; $n:expr) => {{
        let mut v = Vec::with_capacity($n);
        for _ in 0..$n {
            v.push($element);
        }
        v
    }};
}

fn main() {
    let scores = hashmap!{
        "Alice" => 95,
        "Bob" => 87,
        "Charlie" => 92
    };
    println!("成绩: {:?}", scores);
    
    let zeros = vec_of!(0; 5);
    println!("零向量: {:?}", zeros);
}
成绩: {"Charlie": 92, "Alice": 95, "Bob": 87} 零向量: [0, 0, 0, 0, 0]

✅ 验证通过

🏗️ 综合实战:构建器宏

macro_rules! builder {
    ($name:ident { $($field:ident: $ty:ty),* $(,)? }) => {
        pub struct $name {
            $(pub $field: $ty,)*
        }
        
        impl $name {
            pub fn new() -> Self {
                $name {
                    $($field: Default::default(),)*
                }
            }
            $(
                pub fn $field(mut self, value: $ty) -> Self {
                    self.$field = value;
                    self
                }
            )*
        }
    };
}

builder!(ServerConfig {
    host: String,
    port: u16,
    max_connections: usize,
    debug: bool,
});

fn main() {
    let config = ServerConfig::new()
        .host("127.0.0.1".to_string())
        .port(8080)
        .max_connections(100)
        .debug(true);
    
    println!("配置:");
    println!("  host: {}", config.host);
    println!("  port: {}", config.port);
    println!("  max_connections: {}", config.max_connections);
    println!("  debug: {}", config.debug);
}
配置: host: 127.0.0.1 port: 8080 max_connections: 100 debug: true

✅ 验证通过

📝 练习

练习1:print宏

实现自己的print!宏,支持格式化输出。

练习2:enum宏

编写宏自动为枚举实现from_str方法。

🏆 本课成就

🔧 过程宏与声明宏对比

// 声明宏: macro_rules! → 简单模式匹配
// 过程宏: proc-macro → AST变换,更强大

// 实用声明宏: 实现单例模式
macro_rules! singleton {
    ($name:ident: $ty:ty = $init:expr) => {
        static $name: std::sync::OnceLock<$ty> = std::sync::OnceLock::new();
        fn get_$name() -> &$ty {
            $name.get_or_init(|| $init)
        }
    };
}

singleton!(CONFIG: String = String::from("default"));

fn main() {
    println!("配置: {}", get_CONFIG());
    
    // 常用过程宏
    println!("过程宏类型:");
    println!("  派生宏: #[derive(Debug, Clone, Serialize)]");
    println!("  属性宏: #[serde(rename_all = "camelCase")]");
    println!("  函数宏: sqlx::query!("SELECT * FROM users")");
}
配置: default

✅ 验证通过

📊 宏 vs 函数 vs 泛型

特性函数泛型
代码生成✅ 编译期展开❌ 运行时调用✅ 单态化
可变参数❌ 固定参数❌ 固定参数
类型安全⚠️ 模式匹配✅ 编译器检查✅ 编译器检查
调试难度⚠️ 较难✅ 容易✅ 容易
编译速度⚠️ 较慢✅ 快✅ 快
操作语法
// 实用宏: 简化测试创建
macro_rules! test_cases {
    ($func:ident, $( $input:expr => $expected:expr ),*) => {
        $(
            #[test]
            fn concat_idents!(test_, $func, _at, line!())() {
                assert_eq!($func($input), $expected);
            }
        )*
    };
}

// 实用宏: 带时间测量
macro_rules! timed {
    ($label:expr, $block:block) => {{
        let start = std::time::Instant::now();
        let result = $block;
        println!("{}: {:?}", $label, start.elapsed());
        result
    }};
}

fn main() {
    let sum = timed!("1到1亿求和", { (1..=100_000_000u64).sum::() });
    println!("结果: {}", sum);
}
1到1亿求和: X.xxxms 结果: 5000000050000000

✅ 验证通过

🏗️ 综合实战:DSL宏构建

// 构建一个简单的HTML DSL
macro_rules! html {
    ($tag:ident { $($child:tt)* }) => {{
        let tag_name = stringify!($tag);
        let children = html_inner!($($child)*);
        format!("<{}>{}", tag_name, children, tag_name)
    }};
    ($tag:ident $attr:tt { $($child:tt)* }) => {{
        let tag_name = stringify!($tag);
        let attrs = format_attrs!($attr);
        let children = html_inner!($($child)*);
        format!("<{} {}>{}", tag_name, attrs, children, tag_name)
    }};
}

macro_rules! html_inner {
    () => { String::new() };
    ($text:literal $($rest:tt)*) => {{
        let mut s = $text.to_string();
        s.push_str(&html_inner!($($rest)*));
        s
    }};
    ($tag:ident { $($child:tt)* } $($rest:tt)*) => {{
        let mut s = html!($tag { $($child)* });
        s.push_str(&html_inner!($($rest)*));
        s
    }};
}

macro_rules! format_attrs {
    ({ $($key:ident = $val:literal),* }) => {{
        let mut attrs = Vec::new();
        $(
            attrs.push(format!("{}="{}"", stringify!($key), $val));
        )*
        attrs.join(" ")
    }};
}

fn main() {
    // 使用DSL生成HTML
    let page = format!(
        "Rust DSL{}",
        html!{div { "Hello from Rust macro DSL!" }}
    );
    println!("{}", page);
    
    // 简化版 - 直接构建
    let div = format!("
{}
", "Hello, HTML DSL!"); let p = format!("

{}

", "Generated by Rust macros"); println!("{}{}", div, p); }
Hello, HTML DSL!

Generated by Rust macros

✅ 验证通过

📋 宏开发清单

声明宏最佳实践

🔧 调试宏

// 查看宏展开
// cargo expand    - 展开所有宏
// cargo expand specific_function - 展开特定函数

macro_rules! debug_macro {
    ($($token:tt)*) => {
        println!("tokens: {}", stringify!($($token)*));
    };
}

fn main() {
    debug_macro!(hello world 1 + 2);
    
    // 常用调试工具
    println!("宏调试工具:");
    println!("  cargo expand    - 查看宏展开结果");
    println!("  cargo check     - 快速检查语法");
    println!("  trybuild crate  - 测试过程宏");
    println!("  macro_railroad  - 可视化宏规则");
    
    // Rust宏的适用场景
    println!("适用场景:");
    println!("  ✅ DSL(领域特定语言)");
    println!("  ✅ 代码生成(derive宏)");
    println!("  ✅ 减少样板代码");
    println!("  ✅ 编译期计算");
    println!("  ❌ 替代函数/泛型的场景");
}

🏗️ 宏实战:ORM字段宏

// 模拟ORM字段定义宏
macro_rules! define_model {
    ($name:ident { $($field:ident: $ty:ty),* $(,)? }) => {
        #[derive(Debug, Clone)]
        struct $name { $($field: $ty),* }
        
        impl $name {
            fn field_names() -> Vec<&'static str> {
                vec![$(stringify!($field)),*]
            }
            
            fn to_sql_create() -> String {
                let fields: Vec = vec![
                    $(format!("{} TEXT", stringify!($field))),*
                ];
                format!("CREATE TABLE {} ({})", stringify!($name), fields.join(", "))
            }
        }
    };
}

define_model!(User {
    id: String,
    name: String,
    email: String,
});

define_model!(Post {
    id: String,
    title: String,
    content: String,
    author_id: String,
});

fn main() {
    let user = User { id: "1".into(), name: "Alice".into(), email: "a@b.com".into() };
    println!("{:?}", user);
    println!("字段: {:?}", User::field_names());
    println!("SQL: {}", User::to_sql_create());
    
    let post = Post { id: "1".into(), title: "Hello".into(), content: "World".into(), author_id: "1".into() };
    println!("
{:?}", post);
    println!("SQL: {}", Post::to_sql_create());
}
User { id: "1", name: "Alice", email: "a@b.com" } 字段: ["id", "name", "email"] SQL: CREATE TABLE User (id TEXT, name TEXT, email TEXT) Post { id: "1", title: "Hello", content: "World", author_id: "1" } SQL: CREATE TABLE Post (id TEXT, title TEXT, content TEXT, author_id TEXT)

✅ 验证通过

📊 Rust宏发展历程

fn main() {
    println!("Rust宏演进:");
    println!("  2015: macro_rules! 稳定");
    println!("  2019: 过程宏(proc-macro)稳定");
    println!("  2020: 声明宏2.0(macro 2.0)开发中");
    println!("  2024: 更强大的宏模式匹配");
    println!();
    println!("宏使用统计(crates.io):");
    println!("  serde_derive:  #1最受依赖");
    println!("  derive_more:   常用派生宏扩展");
    println!("  thiserror:     错误类型派生");
    println!("  builder_derive: Builder模式派生");
    println!("  strum:         枚举工具宏");
    println!();
    println!("宏调试技巧:");
    println!("  1. cargo expand 查看展开结果");
    println!("  2. println!("{}", stringify!(宏参数)) 查看token");
    println!("  3. trybuild测试过程宏编译错误");
    println!("  4. 逐步构建复杂宏,每步验证");
}

💡 本课要点回顾

Rust的设计哲学贯穿始终:安全、并发、高性能。每个特性都是这三个目标的具体体现。本课所学内容是构建真实Rust应用的基石,务必在实践中反复练习巩固。

记住:Rust的学习曲线虽陡,但一旦掌握,你将获得前所未有的编程信心——编译器就是你的最佳队友。

🔧 宏速查表

// 常用标准库宏
fn main() {
    // 输出宏
    println!("Hello");                    // 换行输出
    print!("No newline");                // 不换行
    eprintln!("Error");                   // 标准错误
    format!("{}", 42);                    // 格式化字符串
    
    // 调试宏
    dbg!(42);                              // 打印并返回值
    dbg!(2 + 3);                           // 打印表达式
    
    // 断言宏
    assert!(true);                         // 断言为真
    assert_eq!(1 + 1, 2);                  // 断言相等
    assert_ne!(1, 2);                      // 断言不等
    
    // 选项宏
    let opt = Some(5);
    if let Some(v) = opt { println!("v={}", v); }
    
    // 编译期宏
    println!("文件: {}", file!());         // 当前文件名
    println!("行号: {}", line!());         // 当前行号
    println!("列号: {}", column!());       // 当前列号
    println!("模块: {}", module_path!());  // 模块路径
    println!("函数: {}", function_name!());// 函数名
    
    // 其他
    todo!("待实现");                       // 未实现标记
    unimplemented!();                       // 未实现
    unreachable!();                         // 不可达代码
    panic!("崩溃");                        // 手动panic
    
    // 向量宏
    let v = vec![1, 2, 3];
    println!("vec: {:?}", v);
    
    // 写入宏
    let mut s = String::new();
    use std::fmt::Write;
    write!(s, "hello").unwrap();
    writeln!(s, " world").unwrap();
    println!("写入: {}", s);
}