惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
B
Blog RSS Feed
云风的 BLOG
云风的 BLOG
G
Google Developers Blog
Recent Announcements
Recent Announcements
A
About on SuperTechFans
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
Google DeepMind News
Google DeepMind News
S
Schneier on Security
S
Secure Thoughts
T
The Exploit Database - CXSecurity.com
Martin Fowler
Martin Fowler
P
Proofpoint News Feed
Security Latest
Security Latest
Jina AI
Jina AI
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Recorded Future
Recorded Future
T
Tor Project blog
有赞技术团队
有赞技术团队
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
News | PayPal Newsroom
博客园 - 三生石上(FineUI控件)
MyScale Blog
MyScale Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
Forbes - Security
Forbes - Security
D
DataBreaches.Net
人人都是产品经理
人人都是产品经理
NISL@THU
NISL@THU
C
Cisco Blogs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Google DeepMind News
Google DeepMind News
Project Zero
Project Zero
IT之家
IT之家
T
Threatpost
Cyberwarzone
Cyberwarzone
O
OpenAI News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
J
Java Code Geeks
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
月光博客
月光博客
Latest news
Latest news
MongoDB | Blog
MongoDB | Blog
Apple Machine Learning Research
Apple Machine Learning Research

博客园 - 若-飞

企业AI Agent落地的核心逻辑与路径 基于langchain,Function Call的成功率怎么解决? LangChain Checkpoint(检查点)是什么?—— Agent 的"存档机制" RAG 设计:Embedding 如何切分 AI 客服系统设计:RAG 知识库设计 Go 百万连接服务器设计:从网卡到业务的全链路解析 深度解析 sync.Pool:从设计哲学到生产实践 Goroutine 泄漏:原因、检测与防范 Go Channel 关闭与超时机制完全指南 Go Map 无限增长问题解决方案 LangChain 聊天记录压缩:原理、机制与实战 揭开 sklearn 文本分类的核心原理:从词袋到逻辑回归 大模型“胡说八道”怎么办?一张图读懂检测、评估与修复全方案 企业级AI知识库权限隔离设计:让AI“懂规矩”比“懂知识”更重要 RAG系统设计全解析:从架构到多模态的核心知识图谱 RAG召回率提升全攻略:7大核心方法让检索更精准 RAG召回率提升秘籍:Metadata过滤的底层原理与实践 构建更好的RAG系统:深入理解混合搜索 一文搞懂 RAG 中 Retriever 和 Reranker 的区别 一文搞懂 RAG 的召回率(Recall)是什么? LangChain / LangGraph、MCP、Harness Engineer 与 Claude Code 的对应关系 Agent Harness 技术笔记:从 Trajectory 到 Function Calling Loop BLEU 是什么?——从原理到工程实践 一文讲清:Approve / Permit / Permit2 的本质区别 分库分表后跨分页查询的完整方案 ai如何处理私有数据 ai幻觉是啥,以及如何解决 别再让大模型“凭空瞎猜”了!带你认识AI最强外挂:ChromaDB 用 useQuery 管请求:TanStack React Query 入门小结 HD钱包--BIP44 TRON 四种 API 面怎么选:从节点协议到 JSON-RPC 再到 TronGrid 以太坊节点存储与共识机制全解析 BSC节点发现协议全解析:UDP发现、Bootnode引导与Gossip交易广播 以太坊节点发现背后的分布式哈希表(DHT)与 Kademlia 原理解析 Solidity中的bytes与string:深入理解这两种特殊的动态数组 智能合约自毁:当资产还在,合约死了 —— 深度解析 selfdestruct 导致的资产锁定风险 TDengine CLI (taos) 使用指南 —— Docker 本地开发实战 在 macOS 上用 DBeaver 连接 TDengine:踩坑总结与最终配置指南 Solidity Storage Slot 深度解析 Geth Snapshot Export/Import 深度解析: 不是备份工具,而是数据分析利器 基于BSC 公链的数据备份与 Snapshot 机制深度解析 Docker 共享内存完全指南:从原理到实践,避免常见的理解误区 Docker容器"僵尸状态"问题排查与自动重启方案 SSE协议深度解析:被低估的HTTP服务器推送标准 TDengine vs MySQL:时序数据处理的时代之选 Proxmox 启用 QEMU Guest Agent 实战指南 解决 Blockscout "batch too large" 错误的完整指南 一文讲清楚什么是基准测试(Benchmark) Docker优雅关闭的艺术:为什么stop_grace_period能防止数据丢失 为什么 Go 没有依赖注入和 Bean 机制?语言设计哲学对比
Rust中的宏(Macro):编译时的代码生成魔法
若-飞 · 2025-12-30 · via 博客园 - 若-飞

引言:当感叹号不只是感叹号

如果你是Rust新手,第一次看到println!("Hello, World!")时,可能会好奇那个感叹号是做什么的。这不是简单的语法装饰,而是Rust最强大的特性之一——宏系统的标志。

// 注意这个感叹号!
println!("Hello, Rust!");      // 这是宏
println("Hello, Rust!");       // 这是错的!函数调用不需要!

// 更多例子
vec![1, 2, 3];                // 创建向量
assert_eq!(x, 5);             // 断言测试
format!("Name: {}", name);    // 格式化字符串

一、宏是什么?简明的定义

宏(Macro)是在编译时执行的代码生成器。 它接收Rust代码作为输入,生成新的Rust代码作为输出。

核心类比

想象你在建房子:

  • 函数 = 熟练的建筑工人(给他砖头,他砌墙)

  •  = 全自动建筑机器人(你给它设计图,它直接造出整面墙,甚至调整设计)

或者更技术化的比喻:

  • 函数处理

  • 处理代码

二、为什么Rust需要宏?

1. 突破函数限制

// 普通函数:参数必须明确
fn add(a: i32, b: i32) -> i32 { a + b }

// 宏:可以接受任意数量和类型的参数
println!("One: {}", 1);
println!("Two: {}, {}", 1, 2);
println!("Three: {}, {}, {}", 1, 2, 3);
// 函数做不到这种灵活性!

2. 减少重复代码(DRY原则)

假设你要为多个结构体实现相同的trait:

// 不用宏:每个结构体都要写一遍
impl Display for Point {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

impl Display for Circle {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "Circle(r={})", self.radius)
    }
}

// 用宏:一次定义,多处使用
macro_rules! impl_display {
    ($type:ty, $format:expr) => {
        impl Display for $type {
            fn fmt(&self, f: &mut Formatter) -> Result {
                write!(f, $format, self)
            }
        }
    };
}

3. 创建领域特定语言(DSL)

// html!宏可以让你在Rust中写HTML-like代码
let page = html! {
    <div class="container">
        <h1>"Hello, World!"</h1>
        <p>"This is HTML in Rust"</p>
    </div>
};

// 这在Web框架中非常有用!

三、宏的两种类型

1. 声明式宏(Declarative Macros)

最常见的形式,使用macro_rules!定义。

// 定义一个简单的vec!宏(类似标准库的实现)
macro_rules! my_vec {
    // 模式1: 创建空向量
    () => {
        Vec::new()
    };
    
    // 模式2: 创建并初始化
    ($($element:expr),*) => {
        {
            let mut v = Vec::new();
            $(v.push($element);)*
            v
        }
    };
    
    // 模式3: 重复元素
    ($element:expr; $count:expr) => {
        {
            let mut v = Vec::with_capacity($count);
            for _ in 0..$count {
                v.push($element.clone());
            }
            v
        }
    };
}

// 使用
let empty: Vec<i32> = my_vec![];          // []
let numbers = my_vec![1, 2, 3];          // [1, 2, 3]
let zeros = my_vec![0; 5];               // [0, 0, 0, 0, 0]

2. 过程宏(Procedural Macros)

更强大、更灵活,但也更复杂。

// 派生宏:自动为结构体生成代码
#[derive(Debug, Clone, Serialize)]
struct User {
    name: String,
    age: u32,
}

// 属性宏:给函数/结构体添加元数据
#[route(GET, "/users")]
fn get_users() { /* ... */ }

// 函数式宏:像函数一样调用,但更强大
json!({"name": "Alice", "age": 30})

四、宏的工作原理:编译时展开

理解宏的关键是明白它在编译时执行

// 你写的代码
let v = vec![1, 2, 3];

// 编译时,宏展开为:
let v = {
    let mut temp_vec = Vec::new();
    temp_vec.push(1);
    temp_vec.push(2);
    temp_vec.push(3);
    temp_vec
};

// println!宏的类似展开
println!("x = {}, y = {}", x, y);
// 展开为复杂的格式化和输出代码

你可以用cargo expand命令查看宏展开后的代码:

$ cargo install cargo-expand
$ cargo expand

五、常用宏速查表

println! / eprintln! 标准输出/错误输出 println!("Hello")format! 格式化字符串 format!("{}", x)vec! 创建向量 vec![1, 2, 3]assert! / assert_eq! 调试断言 assert!(x > 0)unreachable! 标记不应到达的代码 unreachable!()todo! 标记待实现 todo!("implement this")include_str! 嵌入文件内容 include_str!("config.toml")matches! 模式匹配 matches!(value, Some(_))
用途 示例

六、宏 vs 函数:何时使用?

使用宏的场景:

  1. 需要可变数量的参数

  2. 需要操作语法结构(如创建新语法)

  3. 需要在编译时生成代码以减少运行时开销

  4. 实现派生trait(如#[derive(Debug)]

使用函数的场景:

  1. 只需要处理值,不需要处理代码结构

  2. 逻辑相对简单固定

  3. 想要清晰的类型签名

  4. 需要更好的运行时性能(宏展开可能增加代码大小)

七、宏的优缺点

优点:

  • 强大的元编程能力:可以扩展Rust语法

  • 零成本抽象:在编译时完成工作,运行时无开销

  • 消除样板代码:自动生成重复模式

  • 创建DSL:让API更符合人类语言

缺点:

  • 学习曲线陡峭:比函数更难理解和编写

  • 调试困难:错误信息可能指向展开后的代码

  • 编译时间增加:复杂的宏会延长编译时间

  • 可能滥用:过度使用会让代码难以阅读

八、最佳实践

  1. 优先使用函数:除非宏能解决函数无法解决的问题

  2. 保持宏简单:复杂的宏难以理解和维护

  3. 提供清晰的文档:说明宏的用途、参数和展开结果

  4. 充分测试:测试宏的各种使用模式和边界情况

  5. 利用现有宏:标准库和流行crate提供了许多高质量宏

// 好宏:清晰、单一职责
macro_rules! log_error {
    ($msg:expr) => {
        eprintln!("[ERROR] {}", $msg);
    };
}

// 避免创建"魔法"宏
// 不要创建难以理解的隐式行为

九、探索宏的实际应用

想深入了解宏?可以查看这些实际项目:

  1. serde:序列化框架,大量使用派生宏

  2. tokio:异步运行时,使用属性宏定义异步函数

  3. diesel:ORM框架,使用宏创建类型安全的查询

  4. yew / leptos:前端框架,使用宏创建声明式UI

结语:宏是超能力,请谨慎使用

Rust的宏系统是其最独特和强大的特性之一。它让开发者能够在保持零成本抽象的同时,扩展语言本身的能力。

记住这个简单的规则:当你需要编写编写代码的代码时,就该考虑使用宏了。

但正如蜘蛛侠的叔叔所说:"With great power comes great responsibility." 宏是强大的工具,但过度使用会让代码变得难以理解和维护。从使用标准库的宏开始,逐渐理解其原理,最终在真正需要时才创建自己的宏。

进一步学习资源:

  • Rust官方文档:Macros

  • 《Rust编程语言》第19章:宏

  • cargo expand 工具:查看宏展开

  • Rust Playground:在线实验宏

现在,当你再次看到println!("Hello World!")时,你会知道那个感叹号背后是Rust编译时元编程的整个世界在为你工作!