2025年1月1日
Write Powerful Rust Macros
编写强大的 Rust 宏

Write Powerful Rust Macros
书中系统讲解了Rust宏的编写技巧,从简单的声明式宏到复杂的函数式宏,涵盖了多种类型的宏及其应用场景,例如构建DSL(领域特定语言)和改进代码结构。书中还深入探讨了宏的测试和调试方法,并结合实际案例分析了宏在不同项目中的应用,例如构建器模式和基础设施即代码(IaC)。此外,本书还介绍了Rust相关的编程概念,例如纯函数和杂质函数,以及如何利用它们来提高代码质量和可维护性。
这本书《Write Powerful Rust Macros》旨在教导读者如何使用 Rust 的宏进行元编程。本书从元编程的基础概念讲起,逐步深入到各种宏的类型和实际应用,并通过大量的示例和练习,帮助读者掌握编写强大 Rust 宏的技能。
第一章:元编程入门
- 介绍了元编程的概念,即编写可以操作或生成其他代码的代码 [第一章]。
- 讨论了 Rust 中元编程的主要方式——宏,并强调了其重要性 [第一章]。
- 列举了宏的适用场景,如消除重复代码、实现代码生成和抽象复杂模式 [第一章]。
- 同时指出了不应使用宏的情况,如过度使用、复杂逻辑和潜在的性能问题 [第一章]。
- 概述了本书的重点是 Rust 宏 [第一章]。
第二章:声明宏(Declarative Macros)
- 介绍了声明宏的基础知识,包括语法、匹配器(matcher)和转录器(transcriber)。
- 解释了如何使用
macro_rules!
创建声明宏。 - 讨论了代码展开(expanding)的概念,并介绍了
trace_macros!
和log_syntax!
等工具来辅助理解宏的展开过程。 - 讲解了如何使用
$()
来捕获和引用宏变量。 - 说明了如何使用
$()
匹配不同的输入类型,如ident
、literal
、expr
、ty
等。 - 展示了如何使用声明宏实现 DSL (领域特定语言)。
- 解释了如何使用 newtype 来提高类型系统的表达能力。
- 讨论了如何使用声明宏实现递归。
- 介绍了如何通过组合(compose)宏来增强代码的灵活性。
- 讲解了柯里化(currying)的概念,并演示了如何使用宏实现柯里化。
- 讨论了卫生性(hygiene)在宏中的作用,以及如何避免命名冲突。
- 展示了不同括号在宏调用中的使用方法。
第三章:过程宏(Procedural Macros)
- 介绍了过程宏的基本概念和分类:派生宏(derive macros)、属性宏(attribute macros)和函数式宏(function-like macros)。
- 详细讲解了如何设置过程宏项目,包括创建库项目,添加依赖项等。
- 解释了派生宏的工作原理,以及如何使用
#[proc_macro_derive]
注解创建派生宏。 - 说明了如何使用
quote!
宏生成代码。 - 讲解了如何解析输入抽象语法树(AST),并获取结构体的名称。
- 强调了过程宏必须位于库的根目录。
- 提供了一个派生宏的练习。
第四章:使用属性宏修改字段
- 介绍了属性宏的概念,以及如何使用
#[proc_macro_attribute]
注解创建属性宏。 - 讲解了属性宏与派生宏的区别,并说明了属性宏可以修改现有代码。
- 演示了如何使用属性宏来使结构体的字段公开。
- 说明了如何使用
ParseStream
来解析输入流。 - 强调了良好的错误处理在生产级宏中的重要性。
- 介绍了
no-panic
宏的实现原理,以及如何使用宏来证明函数不会发生 panic。 - 提供了一个属性宏的练习。
- 介绍了属性宏的概念,以及如何使用
第五章:函数式宏
- 讲解了函数式宏(function-like macros)的工作原理,以及如何使用
#[proc_macro]
注解创建函数式宏。 - 解释了函数式宏可以使用
!
操作符在代码的几乎任何位置调用。 - 讨论了卫生性在过程宏中的重要性,以及如何使用
call_site
和mixed_site
来控制 span。 - 展示了如何使用函数式宏来扩展语言的功能。
- 强调了不同类型宏的适用场景。
- 介绍了
format_ident!
宏,用于动态创建标识符。
- 讲解了函数式宏(function-like macros)的工作原理,以及如何使用
第六章:测试构建器宏
- 通过一个构建器宏的例子,详细讲解了如何测试过程宏。
- 介绍了白盒测试和黑盒测试的概念,并说明了它们在宏测试中的应用。
- 演示了如何使用
quote!
宏生成测试代码。 - 强调了测试驱动开发(TDD)在宏开发中的重要性。
- 介绍了cargo expand 工具,用于查看宏展开后的代码。
- 讨论了不同类型的测试,如单元测试、集成测试、端到端测试和冒烟测试。
- 展示了如何使用 trybuild 包进行测试。
- 总结了构建器模式,并强调了单元测试的重要性。
第七章:错误处理
- 讨论了异常的缺点,并介绍了函数式编程中纯函数的概念。
- 讲解了使用
Result
枚举来处理错误。 - 强调了
panic!
的使用场景,并说明了unwrap
和expect
方法在实验性代码中的作用。 - 演示了如何使用属性宏来修改函数,并将其中的
panic!
转换为Result
。 - 说明了如何使用
syn::Error
来创建编译时错误。 - 介绍了
abort
和abort_call_site
宏,用于生成详细的错误信息。 - 展示了如何使用
proc_macro_error
crate 进行错误处理。
第八章:宏的灵活性
- 讲解了如何使用属性来扩展宏的功能。
- 介绍了如何使用
syn::meta
来解析属性信息,包括NamedValue
、Path
和List
等类型。 - 讨论了
lazy
和eager
求值的概念,并说明了如何使用unwrap_or_else
来进行延迟求值。 - 介绍了
Default
trait,以及如何使用默认值作为宏的备选方案。 - 讨论了如何使用 type state 模式来增强类型系统的表达能力,并避免不合法的状态。
- 展示了如何将构建器模式与类型状态模式相结合。
- 介绍了其他类型的属性,如外部属性、内部属性和文档注释。
第九章:编写基础设施 DSL
- 介绍了 IaC(基础设施即代码) 的概念,并说明了如何使用宏创建 DSL。
- 演示了如何使用函数式宏来解析 DSL 输入,并将其转换为 Rust 代码。
- 讲解了如何使用
syn
库的Parse
trait 来解析输入流。 - 说明了如何使用
lookahead1
来查看下一个 token 并返回错误。 - 介绍了两种其他的解析方法,并使用了
Punctuated
结构体和构建器模式。 - 展示了如何在宏中使用异步代码来创建 AWS 基础设施。
- 讨论了如何使用
thiserror
crate 来简化错误处理。 - 强调了对宏进行端到端测试的重要性。
第十章:宏与外部世界
- 讨论了如何在一个库中暴露多个宏。
- 介绍了如何使用 feature flags 来添加或禁用宏的功能。
- 说明了如何使用属性来控制生成代码。
- 强调了为宏编写文档的重要性,并介绍了如何使用 doctest。
- 讨论了如何发布宏库,以及开源的意义。
- 总结了本书的内容,并鼓励读者继续探索宏的世界。
附录:练习解答
- 提供了所有章节的练习解答。
重要概念总结
- 宏(Macros): Rust 中进行元编程的主要工具,用于在编译时生成代码。
- 声明宏(Declarative Macros): 基于模式匹配和代码替换的宏,使用
macro_rules!
定义。 - 过程宏(Procedural Macros): 使用 Rust 代码编写的宏,可以执行更复杂的代码生成逻辑。包括派生宏、属性宏和函数式宏。
- AST(抽象语法树): 代码的结构化表示,用于过程宏进行代码分析和修改。
quote!
宏: 用于生成 Rust 代码的宏。syn
库: 用于解析 Rust 代码的库。proc_macro
和proc_macro2
: 用于编写过程宏的库。- DSL(领域特定语言): 为特定领域设计的语言。
- 类型状态模式 (Type State Pattern): 使用类型参数来编码系统的状态,避免不合法的状态转换。
- 卫生性(Hygiene): 宏中避免命名冲突的机制。
- Result: 用于处理可能失败的操作的枚举类型
- 单元测试: 用于测试代码的最小单元的测试方法。
- 白盒测试: 知道代码内部实现的测试方法
- 黑盒测试: 不知道代码内部实现,只关注输入和输出的测试方法。