Rust: Rust 宏(Macros)入门:声明宏与元编程

最后更新:2026-08-26

宏是 Rust 的"代码印钞机"——写一次宏,自动生成无数重复代码,让程序员从复制粘贴的苦海中解脱。

如果说函数是"把逻辑封装起来重复调用",那宏就是"把代码生成规则封装起来重复展开"。函数操作的是运行时值,宏操作的是编译期代码本身。这就像:函数是工厂流水线(输入原料,输出产品),宏是工厂建设图纸(输入设计图,输出整条流水线)。


1. 你将学到


2. 印钞机的故事

(1) 痛苦:重复代码的泥潭

Tom 是一家物流公司的 Rust 工程师。他被分配了一个看似简单的任务:为 5 种运输方式(卡车、轮船、飞机、火车、无人机)各写一个计算运费和预估时间的函数。

结果呢?

"如果有个办法写一次规则,自动生成所有类似函数就好了……"

(2) Rust 宏的方案

RUST
// 用宏来定义"运费计算器"的模板
macro_rules! create_shipping_calculator {
    // 匹配模式:运输方式名称 + 每公里费率 + 速度
    ($name:ident, $rate_per_km:expr, $speed:expr) => {
        fn $name(distance: f64) -> (f64, f64) {
            let base_cost = distance * $rate_per_km;
            let fuel_surcharge = base_cost * 0.1;
            let total = base_cost + fuel_surcharge;
            let time_hours = distance / $speed;
            (total, time_hours)
        }
    };
}

// 一行宏调用 = 生成一个完整函数
create_shipping_calculator!(truck,  1.5,  60.0);
create_shipping_calculator!(ship,   0.8,  30.0);
create_shipping_calculator!(plane,  5.0, 800.0);
create_shipping_calculator!(train,  1.2,  80.0);
create_shipping_calculator!(drone,  2.0,  50.0);

fn main() {
    let distance = 500.0;
    // 每个函数都自动存在,就像手写的一样
    println!("Truck :  cost=${:.2}, time={:.1}h", truck(distance).0,  truck(distance).1);
    println!("Ship  :  cost=${:.2}, time={:.1}h", ship(distance).0,   ship(distance).1);
    println!("Plane :  cost=${:.2}, time={:.1}h", plane(distance).0,  plane(distance).1);
    println!("Train :  cost=${:.2}, time={:.1}h", train(distance).0,  train(distance).1);
    println!("Drone :  cost=${:.2}, time={:.1}h", drone(distance).0,  drone(distance).1);
}

输出:

TEXT 📖 仅展示
Truck :  cost=$825.00, time=8.3h
Ship  :  cost=$440.00, time=16.7h
Plane :  cost=$2750.00, time=0.6h
Train :  cost=$660.00, time=6.2h
Drone :  cost=$1100.00, time=10.0h

宏就像"代码印钞机":你设计好模板(印钞版),然后每次调用宏就"印刷"出一份完整的代码。修改时只需要改模板一处,所有生成的代码同步更新——再也不用逐个函数手动修改了。


3. 核心概念

(1) 声明宏展开过程

100%
graph TB
    A[macro_rules! 声明宏] --> B[匹配臂 1: 模式 => 模板]
    A --> C[匹配臂 2: 模式 => 模板]
    A --> D[匹配臂 N: 模式 => 模板]

    B --> E[编译器匹配 input token]
    C --> E
    D --> E

    E --> F{匹配成功?}
    F -->|Yes| G[替换模板并展开代码]
    F -->|No| H[编译错误: 不匹配]

    G --> I[生成 AST 节点]
    I --> J[继续编译]

    style A fill:#4a90d9,color:#fff
    style E fill:#e6a23c,color:#fff
    style F fill:#f56c6c,color:#fff
    style G fill:#67c23a,color:#fff

(2) 函数 vs 宏对比

维度 函数 (fn) 宏 (macro_rules!)
执行时机 运行时调用 编译期展开
参数数量 固定 可变(通过重复模式)
参数类型 固定类型 任意 token 流
代码生成 不生成代码 生成新的代码 AST
返回值 有返回值类型 可生成任意代码片段
调用方式 foo(args) foo!(args) 带感叹号
递归限制 栈深度 宏递归深度(默认 128 层)
卫生性 天然隔离作用域 声明宏部分卫生

(3) 常见内置宏

作用 示例
println! 打印到 stdout 并换行 println!("Hello, {}!", name)
print! 打印到 stdout 不换行 print!("count: {}", i)
format! 格式化字符串返回 String let s = format!("{}点{}分", h, m)
vec! 快速创建 Vec let v = vec![1, 2, 3]
todo! 占位标记,运行时 panic fn foo() { todo!() }
unimplemented! 未实现标记,运行时 panic fn bar() { unimplemented!() }
eprintln! 打印到 stderr eprintln!("Error: {}", msg)
write! 写入实现了 fmt::Write 的类型 write!(&mut s, "{}", val)
concat! 编译期字符串拼接 concat!("a", "b", "c")"abc"
stringify! 将表达式转为字符串字面量 stringify!(1+2)"1 + 2"

(4) 声明宏 vs 过程宏对比

维度 声明宏 macro_rules! 过程宏(Proc Macro)
定义方式 macro_rules! name { ... } 独立 crate + #[proc_macro_*]
展开时机 编译期模式匹配 + 替换 编译期过程式代码生成
能力 模式匹配 + token 替换 可读取/生成任意 AST
复杂度 低(声明式) 高(需写 Rust 代码处理 AST)
子分类 派生宏 #[derive] / 属性宏 #[attr] / 函数式宏 name!()
典型用途 vec![]println! serde::Serializetokio::main
调试难度 中等 较高

4. 宏示例

▶ 示例 1:第一个声明宏——自动生成 getter 函数(难度 ⭐)

RUST
// ============================================
// 使用 macro_rules! 定义宏
// 场景:为结构体字段自动生成 getter 方法
// ============================================

// 宏定义:根据字段名和类型生成 getter 函数
// 宏名后跟 ! 表示这是一个宏
macro_rules! create_getter {
    // 匹配模式:$name 是标识符(ident),$ty 是类型(ty)
    // => 后面的代码块是展开模板
    ($name:ident, $ty:ty) => {
        pub fn $name(&self) -> $ty {
            self.$name.clone()
        }
    };
}

// 使用宏的结构体
#[derive(Debug)]
struct Student {
    name: String,
    age: u32,
    grade: String,
}

impl Student {
    // 手动写的 getter —— 一个字段就要写一个
    // pub fn name(&self) -> String { self.name.clone() }
    // pub fn age(&self) -> u32 { self.age }
    // pub fn grade(&self) -> String { self.grade.clone() }

    // 用宏自动生成 —— 一行一个 getter
    create_getter!(name, String);
    create_getter!(age, u32);
    create_getter!(grade, String);
}

fn main() {
    let s = Student {
        name: "Alice".to_string(),
        age: 20,
        grade: "A".to_string(),
    };

    // 调用宏生成的 getter 方法
    println!("Name  : {}", s.name());
    println!("Age   : {}", s.age());
    println!("Grade : {}", s.grade());
}

输出:

TEXT 📖 仅展示
Name  : Alice
Age   : 20
Grade : A

create_getter! 接收一个字段名和一个类型,展开成一个完整的 pub fn 定义。三个 create_getter! 调用展开后等价于手写三个 getter 函数。这就是"代码印钞机"的雏形——模板定义了代码生成规则,每次调用印出一份代码。


▶ 示例 2:重复模式 $()*$()+——可变参数宏(难度 ⭐⭐)

RUST
// ============================================
// 演示宏的重复模式:$()* 零次或多次、$()+ 一次或多次
// 场景:一个迷你测试框架,支持多个断言
// ============================================

// 宏:运行多个测试用例,每个用例包含名称 + 表达式 + 期望值
// $()* 表示括号内的模式可以重复零次或多次
macro_rules! run_tests {
    // 每个测试由 (名称, 表达式, 期望值) 三元组组成
    // $test_name 是标识符,$expr 是表达式,$expected 是表达式
    ($( $test_name:ident, $expr:expr, $expected:expr );* $(;)?) => {
        $(
            println!("[Test] {} ...", stringify!($test_name));
            let result = $expr;
            let expected: i32 = $expected;
            if result == expected {
                println!("  ✅ PASS: {} == {}", result, expected);
            } else {
                println!("  ❌ FAIL: {} != {} (expected {})", stringify!($expr), result, expected);
            }
        )*
    };
}

// 宏:计算任意数量数字的和
// $()+ 表示括号内的模式至少重复一次
macro_rules! sum_of {
    // 使用 $()+ 要求至少一个参数
    ($($x:expr),+ $(,)?) => {
        // 0 + $x 的累加: 0 + a + b + c ...
        {
            let mut sum = 0i64;
            $(
                sum += $x as i64;
            )+
            sum
        }
    };
}

fn main() {
    println!("=== Mini Test Framework ===");

    // 调用 run_tests! 宏 —— 传入多个测试用例
    run_tests! {
        add_one,    1 + 1, 2;
        multiply,   3 * 4, 12;
        subtract,   10 - 3, 7;
        power,      2 * 2 * 2, 8
    }

    println!("\n=== Sum Calculator ===");

    // 调用 sum_of! 宏 —— 传入任意数量参数
    let s1 = sum_of!(1, 2, 3, 4, 5);
    println!("sum_of!(1..5) = {}", s1);

    let s2 = sum_of!(10, 20, 30);
    println!("sum_of!(10,20,30) = {}", s2);

    // 单个参数也可以
    let s3 = sum_of!(42);
    println!("sum_of!(42) = {}", s3);

    println!("\n=== Done ===");
}

输出:

TEXT 📖 仅展示
=== Mini Test Framework ===
[Test] add_one ...
  ✅ PASS: 2 == 2
[Test] multiply ...
  ✅ PASS: 12 == 12
[Test] subtract ...
  ✅ PASS: 7 == 7
[Test] power ...
  ✅ PASS: 8 == 8

=== Sum Calculator ===
sum_of!(1..5) = 15
sum_of!(10,20,30) = 60
sum_of!(42) = 42

=== Done ===

$()*$()+ 是宏实现"可变参数"的关键。$()* 表示"零次或多次重复"(像 Vec 可以为空),$()+ 表示"一次或多次重复"(至少有一个参数)。重复模式内部还可以用分隔符(, ; 等)来控制参数的分组方式。


▶ 示例 3:vec! 宏的简化实现——理解内置宏原理(难度 ⭐⭐)

RUST
// ============================================
// 模拟实现一个简化版 vec! 宏
// 理解 vec! 宏的底层展开原理
// ============================================

// 简化版 vec! 宏 —— 不支持 vec![x; n] 语法
macro_rules! my_vec {
    // 空向量
    () => {
        Vec::new()
    };
    // 单个元素
    ($elem:expr) => {
        {
            let mut v = Vec::new();
            v.push($elem);
            v
        }
    };
    // 多个元素,用逗号分隔
    ($($x:expr),+ $(,)?) => {
        {
            let mut v = Vec::new();
            $(
                v.push($x);
            )+
            v
        }
    };
}

fn main() {
    // 使用内置 vec! 宏
    let builtin_empty: Vec<i32> = vec![];
    let builtin_one = vec![42];
    let builtin_multi = vec![1, 2, 3, 4, 5];

    println!("=== Built-in vec! ===");
    println!("empty  : {:?}", builtin_empty);
    println!("one    : {:?}", builtin_one);
    println!("multi  : {:?}", builtin_multi);

    // 使用自定义 my_vec! 宏
    let my_empty: Vec<i32> = my_vec![];
    let my_one = my_vec![42];
    let my_multi = my_vec![10, 20, 30, 40, 50];

    println!("\n=== Custom my_vec! ===");
    println!("my_empty : {:?}", my_empty);
    println!("my_one   : {:?}", my_one);
    println!("my_multi : {:?}", my_multi);

    // 验证功能一致
    assert_eq!(builtin_multi.len(), 5);
    assert_eq!(my_multi.len(), 5);
    assert_eq!(builtin_multi, vec![1, 2, 3, 4, 5]);
    assert_eq!(my_multi, vec![10, 20, 30, 40, 50]);

    println!("\n=== All assertions passed! ===");
}

输出:

TEXT 📖 仅展示
=== Built-in vec! ===
empty  : []
one    : [42]
multi  : [1, 2, 3, 4, 5]

=== Custom my_vec! ===
my_empty : []
my_one   : [42]
my_multi : [10, 20, 30, 40, 50]

=== All assertions passed! ===

你看到的 vec! 宏其实就是一个 macro_rules! 声明宏!它通过 $($x:expr),+ 匹配逗号分隔的表达式列表,然后展开为 v.push($x) 的重复代码块。这就是"函数做不到的事"——vec![1, 2, 3] 如果写成函数,不可能在编译期确定元素个数并生成对应的 push 代码。


▶ 示例 4:内置宏 todo!unimplemented! 的使用(难度 ⭐)

RUST
// ============================================
// 演示内置宏:todo! / unimplemented! / format! / eprintln!
// 场景:一个正在开发中的库存管理系统
// ============================================

// 模拟库存项
#[derive(Debug)]
struct InventoryItem {
    id: u32,
    name: String,
    quantity: u32,
}

// 库存管理器
struct InventoryManager {
    items: Vec<InventoryItem>,
}

impl InventoryManager {
    fn new() -> InventoryManager {
        InventoryManager {
            items: Vec::new(),
        }
    }

    // 已实现:添加商品
    fn add_item(&mut self, id: u32, name: &str, quantity: u32) {
        self.items.push(InventoryItem {
            id,
            name: name.to_string(),
            quantity,
        });
        // 使用 format! 宏格式化日志消息
        let log_msg = format!("[INFO] Added item: {} (id={}, qty={})", name, id, quantity);
        println!("{}", log_msg);
    }

    // 已实现:查找商品
    fn find_item(&self, id: u32) -> Option<&InventoryItem> {
        self.items.iter().find(|item| item.id == id)
    }

    // 未实现:更新库存
    fn update_quantity(&mut self, _id: u32, _new_qty: u32) {
        // TODO: 实现库存更新逻辑
        // todo!() 会 panic 并提示"未实现"
        todo!("update_quantity: id={} quantity={}", _id, _new_qty);
    }

    // 未实现:生成库存报告
    fn generate_report(&self) -> String {
        // unimplemented!() 表示这个功能还没做
        unimplemented!("generate_report() is not yet implemented");
    }

    // 已实现:打印所有商品
    fn list_items(&self) {
        if self.items.is_empty() {
            println!("  (no items in inventory)");
            return;
        }
        for item in &self.items {
            println!("  #{} {} (qty: {})", item.id, item.name, item.quantity);
        }
    }
}

fn main() {
    let mut manager = InventoryManager::new();

    println!("=== Inventory Manager ===");

    // 添加商品
    manager.add_item(101, "Laptop", 10);
    manager.add_item(102, "Mouse", 50);
    manager.add_item(103, "Keyboard", 30);

    // 列出商品
    println!("\nCurrent inventory:");
    manager.list_items();

    // 查找商品
    if let Some(item) = manager.find_item(102) {
        println!("\nFound: {:?}", item);
    }

    // 以下代码取消注释会 panic(但不会编译错误):
    // manager.update_quantity(101, 8);   // panics: "not yet implemented"
    // let report = manager.generate_report(); // panics: "not yet implemented"

    println!("\n=== Demo completed ===");
    println!("Note: Try uncommenting update_quantity() or generate_report() to see todo!/unimplemented! in action.");
}

输出:

TEXT 📖 仅展示
=== Inventory Manager ===
[INFO] Added item: Laptop (id=101, qty=10)
[INFO] Added item: Mouse (id=102, qty=50)
[INFO] Added item: Keyboard (id=103, qty=30)

Current inventory:
  #101 Laptop (qty: 10)
  #102 Mouse (qty: 50)
  #103 Keyboard (qty: 30)

Found: InventoryItem { id: 102, name: "Mouse", quantity: 50 }

=== Demo completed ===
Note: Try uncommenting update_quantity() or generate_report() to see todo!/unimplemented! in action.

todo!()unimplemented!() 是 Rust 开发者的"占位符利器"。todo!() 可以带一条描述信息(todo!("msg: {}", val)),适合在开发中途标记未完成的功能。unimplemented!() 更适合标记接口定义中暂不实现的方法。两者都会在运行时 panic,但不会导致编译错误——让你可以先把代码写完整再逐步实现。


▶ 示例 5:宏的卫生性(Hygiene)——宏内部变量不污染外部(难度 ⭐⭐⭐)

RUST
// ============================================
// 演示宏的卫生性(hygiene)
// 宏内部创建的变量不会与外部作用域冲突
// ============================================

// 注意:Rust 声明宏的卫生性是"部分卫生"的
// 宏内部用 $ 捕获的变量名不会与外部冲突
// 但宏内部直接写的标识符在 Rust 2018+ 中有特殊规则

// 宏:创建一个局部变量 tmp 并做交换
// 注意:宏内部直接写 tmp 是卫生的——不会影响外部的 tmp
macro_rules! swap_with_tmp {
    ($a:expr, $b:expr) => {
        {
            let tmp = $a;
            $a = $b;
            $b = tmp;
        }
    };
}

// 宏:演示卫生性 —— 即使外部也有 tmp 变量
macro_rules! demonstrate_hygiene {
    ($x:expr) => {
        {
            // 宏内部定义的 tmp 是卫生的
            let tmp = $x * 2;
            println!("  Inside macro: tmp = {}", tmp);
            tmp
        }
    };
}

// 非卫生的反例(用另一种方式展示)
// 注意:Rust 声明宏不允许直接创建跨作用域冲突的变量
// 所以这里用 concat 模拟"非卫生"可能导致的问题

fn main() {
    println!("=== Macro Hygiene Demonstration ===\n");

    // 场景 1:宏内部变量不影响外部
    println!("1. Macro internal variable vs external variable:");
    let mut x = 10;
    let mut y = 20;

    println!("  Before swap: x={}, y={}", x, y);

    // 宏内部使用了 tmp,但外部的变量名 tmp 不受影响
    swap_with_tmp!(x, y);

    println!("  After swap:  x={}, y={}", x, y);

    // 外部的 tmp 变量并不存在 —— 宏内部的 tmp 是卫生的
    // 下面这行取消注释会编译错误:
    // println!("tmp from macro = {}", tmp);  // ❌ 编译错误: tmp not found

    // 场景 2:宏内部变量与外部同名变量不冲突
    println!("\n2. Hygiene with same name:");
    let tmp = 100;  // 外部 tmp
    println!("  Outside macro: tmp = {}", tmp);

    let result = demonstrate_hygiene!(5);
    println!("  Return value: {}", result);
    println!("  Outside macro again: tmp = {}", tmp);  // 仍然是 100,不受宏影响

    // 场景 3:为什么卫生性重要
    println!("\n3. Why hygiene matters:");
    println!("  Without hygiene, macros could accidentally:");
    println!("  - Overwrite variables in the caller's scope");
    println!("  - Create hard-to-find bugs");
    println!("  - Break encapsulation of the calling code");
    println!("  Rust's hygiene prevents these issues at compile time.");

    println!("\n=== Demo completed ===");
}

输出:

TEXT 📖 仅展示
=== Macro Hygiene Demonstration ===

1. Macro internal variable vs external variable:
  Before swap: x=10, y=20
  After swap:  x=20, y=10

2. Hygiene with same name:
  Outside macro: tmp = 100
  Inside macro: tmp = 10
  Return value: 10
  Outside macro again: tmp = 100

3. Why hygiene matters:
  Without hygiene, macros could accidentally:
  - Overwrite variables in the caller's scope
  - Create hard-to-find bugs
  - Break encapsulation of the calling code
  Rust's hygiene prevents these issues at compile time.

宏的卫生性(hygiene)是 Rust 声明宏的一个重要特性:宏内部创建的变量名不会"泄漏"到调用者的作用域中。在上面的示例中,宏内部有 tmp,外部也有 tmp,但它们互不干扰。这避免了 C 语言宏中常见的"名字冲突"问题——在 C 里,如果宏内部用了一个叫 tmp 的变量,而调用者恰好也有一个 tmp,就会产生难以排查的 Bug。


▶ 示例 6:综合示例——用宏构建迷你测试框架(难度 ⭐⭐⭐)

RUST
// ============================================
// 综合示例:用宏构建一个迷你单元测试框架
// 结合使用:匹配模式、重复模式、内置宏
// ============================================

// 测试结果统计
struct TestStats {
    total: u32,
    passed: u32,
    failed: u32,
}

impl TestStats {
    fn new() -> TestStats {
        TestStats { total: 0, passed: 0, failed: 0 }
    }

    fn print_summary(&self) {
        println!("\n==============================");
        println!("Test Summary:");
        println!("  Total : {}", self.total);
        println!("  Passed: {}", self.passed);
        println!("  Failed: {}", self.failed);
        if self.failed == 0 {
            println!("  ✅ All tests passed!");
        } else {
            println!("  ❌ {} test(s) failed", self.failed);
        }
        println!("==============================");
    }
}

// 宏:定义一组测试用例
// 每个测试由名称、断言表达式、期望结果组成
macro_rules! test_suite {
    // 匹配零个或多个测试
    ($( $name:ident: $left:expr, $op:tt, $right:expr );* $(;)?) => {{
        let mut stats = TestStats::new();
        $(
            stats.total += 1;
            print!("[Test] {}: {} {} {} ... ", stringify!($name),
                stringify!($left), stringify!($op), stringify!($right));

            let passed = match $op {
                == => { $left == $right }
                != => { $left != $right }
                <  => { $left < $right }
                <= => { $left <= $right }
                >  => { $left > $right }
                >= => { $left >= $right }
                _ => { panic!("Unsupported operator: {}", stringify!($op)); }
            };

            if passed {
                stats.passed += 1;
                println!("✅ PASS");
            } else {
                stats.failed += 1;
                println!("❌ FAIL (got {:?}, expected {:?})", $left, $right);
            }
        )*
        stats
    }};
}

fn main() {
    println!("=== Mini Test Framework ===");
    println!("Using macro-generated test suite\n");

    // 用宏定义测试套件
    let stats = test_suite! {
        test_add:  2 + 2, ==, 4;
        test_sub:  10 - 3, ==, 7;
        test_mul:  3 * 4, ==, 12;
        test_div:  10 / 2, ==, 5;
        test_gt:   100, >, 50;
        test_lt:   3, <, 10;
        test_eq:   "hello", ==, "hello";
        test_neq:  42, !=, 0
    };

    // 打印统计
    stats.print_summary();

    // 验证所有测试通过
    assert_eq!(stats.total, 8);
    assert_eq!(stats.passed, 8);
    assert_eq!(stats.failed, 0);

    println!("\n=== Demo completed ===");
}

输出:

TEXT 📖 仅展示
=== Mini Test Framework ===
Using macro-generated test suite

[Test] test_add: 2 + 2 == 4 ... ✅ PASS
[Test] test_sub: 10 - 3 == 7 ... ✅ PASS
[Test] test_mul: 3 * 4 == 12 ... ✅ PASS
[Test] test_div: 10 / 2 == 5 ... ✅ PASS
[Test] test_gt: 100 > 50 ... ✅ PASS
[Test] test_lt: 3 < 10 ... ✅ PASS
[Test] test_eq: "hello" == "hello" ... ✅ PASS
[Test] test_neq: 42 != 0 ... ✅ PASS

==============================
Test Summary:
  Total : 8
  Passed: 8
  Failed: 0
  ✅ All tests passed!
==============================

=== Demo completed ===

这个综合示例展示了宏的真正威力:test_suite! 宏接收一组测试定义(名称、表达式、比较运算符、期望值),自动展开成完整的测试执行代码。8 个测试用例,用宏写只需要 8 行调用,而手写等效代码至少需要 60 行。更重要的是——如果需要增加"测试超时"或"测试分组"功能,只需要改宏模板一处,所有测试自动升级。


❓ 常见问题

Q 宏和函数到底有什么区别?什么时候用宏而不是函数?
A 宏在编译期展开代码,函数在运行时执行。
Q macro_rules! 中的 $()*$()+ 有什么区别?
A $()* 匹配零次或多次重复(允许空),$()+ 匹配一次或多次重复(至少一次)。
Q 什么是宏的卫生性(hygiene)?为什么重要?
A 卫生性是指宏内部创建的变量不会与外部作用域的变量冲突。
Q todo!()unimplemented!() 有什么区别?
A 两者都在运行时 panic,但语义不同。
Q 什么是过程宏(procedural macro)?
A 过程宏是比声明宏更强大的宏系统,它不是"匹配替换",而是"接收代码 → 运行 Rust 函数 → 输出新代码"。

📖 小节


📝 作业

  1. 难度 ⭐:写一个 make_pair! 宏,接收两个表达式,返回一个元组 (expr1, expr2)。例如 make_pair!(42, "hello") 展开为 (42, "hello")。在 main 中调用并打印结果。
  2. 难度 ⭐⭐:写一个 assert_equal! 宏,接收两个表达式,如果它们相等就打印 ✅ PASS,否则打印 ❌ FAIL: left != right。使用 stringify! 宏来打印表达式原文。创建至少 3 个测试用例(包含相等和不相等的情况)并运行。
  3. 难度 ⭐⭐⭐:写一个 create_enum_with_display! 宏,接收枚举名称和一组变体名称,自动生成枚举定义 + 实现 Display trait(每个变体显示为对应的字符串)。例如 create_enum_with_display!(Color, Red, Green, Blue) 展开为一个 Color 枚举,其中 Red 显示为 "Red"。提示:使用 $()* 重复模式和 stringify!
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏