2025年1月1日

Write Powerful Rust Macros

编写强大的 Rust 宏
书籍封面

Write Powerful Rust Macros

作者:Sam Van Overmeire 出版日期:2024-07-16 出版社:Manning
书中系统讲解了Rust宏的编写技巧,从简单的声明式宏到复杂的函数式宏,涵盖了多种类型的宏及其应用场景,例如构建DSL(领域特定语言)和改进代码结构。书中还深入探讨了宏的测试和调试方法,并结合实际案例分析了宏在不同项目中的应用,例如构建器模式和基础设施即代码(IaC)。此外,本书还介绍了Rust相关的编程概念,例如纯函数和杂质函数,以及如何利用它们来提高代码质量和可维护性。

这本书《Write Powerful Rust Macros》旨在教导读者如何使用 Rust 的宏进行元编程。本书从元编程的基础概念讲起,逐步深入到各种宏的类型和实际应用,并通过大量的示例和练习,帮助读者掌握编写强大 Rust 宏的技能。

  • 第一章:元编程入门

    • 介绍了元编程的概念,即编写可以操作或生成其他代码的代码 [第一章]。
    • 讨论了 Rust 中元编程的主要方式——,并强调了其重要性 [第一章]。
    • 列举了宏的适用场景,如消除重复代码、实现代码生成和抽象复杂模式 [第一章]。
    • 同时指出了不应使用宏的情况,如过度使用、复杂逻辑和潜在的性能问题 [第一章]。
    • 概述了本书的重点是 Rust 宏 [第一章]。
  • 第二章:声明宏(Declarative Macros)

    • 介绍了声明宏的基础知识,包括语法、匹配器(matcher)和转录器(transcriber)。
    • 解释了如何使用 macro_rules! 创建声明宏。
    • 讨论了代码展开(expanding)的概念,并介绍了 trace_macros!log_syntax! 等工具来辅助理解宏的展开过程。
    • 讲解了如何使用 $() 来捕获和引用宏变量。
    • 说明了如何使用 $() 匹配不同的输入类型,如 identliteralexprty 等。
    • 展示了如何使用声明宏实现 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_sitemixed_site 来控制 span。
    • 展示了如何使用函数式宏来扩展语言的功能。
    • 强调了不同类型宏的适用场景。
    • 介绍了 format_ident! 宏,用于动态创建标识符。
  • 第六章:测试构建器宏

    • 通过一个构建器宏的例子,详细讲解了如何测试过程宏。
    • 介绍了白盒测试黑盒测试的概念,并说明了它们在宏测试中的应用。
    • 演示了如何使用 quote! 宏生成测试代码。
    • 强调了测试驱动开发(TDD)在宏开发中的重要性。
    • 介绍了cargo expand 工具,用于查看宏展开后的代码。
    • 讨论了不同类型的测试,如单元测试、集成测试、端到端测试和冒烟测试。
    • 展示了如何使用 trybuild 包进行测试。
    • 总结了构建器模式,并强调了单元测试的重要性。
  • 第七章:错误处理

    • 讨论了异常的缺点,并介绍了函数式编程中纯函数的概念。
    • 讲解了使用 Result 枚举来处理错误。
    • 强调了 panic! 的使用场景,并说明了 unwrapexpect 方法在实验性代码中的作用。
    • 演示了如何使用属性宏来修改函数,并将其中的 panic! 转换为 Result
    • 说明了如何使用 syn::Error 来创建编译时错误。
    • 介绍了 abortabort_call_site 宏,用于生成详细的错误信息。
    • 展示了如何使用 proc_macro_error crate 进行错误处理。
  • 第八章:宏的灵活性

    • 讲解了如何使用属性来扩展宏的功能。
    • 介绍了如何使用 syn::meta 来解析属性信息,包括 NamedValuePathList 等类型。
    • 讨论了 lazyeager 求值的概念,并说明了如何使用 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_macroproc_macro2: 用于编写过程宏的库。
    • DSL(领域特定语言): 为特定领域设计的语言。
    • 类型状态模式 (Type State Pattern): 使用类型参数来编码系统的状态,避免不合法的状态转换。
    • 卫生性(Hygiene): 宏中避免命名冲突的机制。
    • Result: 用于处理可能失败的操作的枚举类型
    • 单元测试: 用于测试代码的最小单元的测试方法。
    • 白盒测试: 知道代码内部实现的测试方法
    • 黑盒测试: 不知道代码内部实现,只关注输入和输出的测试方法。