291次发布 (184个稳定版)

2.0.75 2024年8月17日
2.0.72 2024年7月21日
2.0.57 2024年3月30日
2.0.43 2023年12月25日
0.10.3 2016年11月11日

#10 in 过程宏

Download history 5880945/week @ 2024-05-02 5777130/week @ 2024-05-09 5933453/week @ 2024-05-16 5675682/week @ 2024-05-23 5993768/week @ 2024-05-30 5859153/week @ 2024-06-06 6142917/week @ 2024-06-13 6113795/week @ 2024-06-20 5891452/week @ 2024-06-27 5739307/week @ 2024-07-04 6264607/week @ 2024-07-11 6359484/week @ 2024-07-18 6431894/week @ 2024-07-25 6593806/week @ 2024-08-01 7186101/week @ 2024-08-08 7046925/week @ 2024-08-15

28,495,868 每月下载量
97,897 个crates中使用了(9,060直接使用)

MIT/Apache

1.5MB
40K SLoC

Rust源代码解析器

github crates.io docs.rs build status

Syn是一个解析库,用于将Rust令牌流解析为Rust源代码的语法树。

目前这个库主要用于Rust过程宏,但包含了一些可能在更广泛范围内有用的API。

  • 数据结构 — Syn提供了一个完整的语法树,可以表示任何有效的Rust源代码。语法树以syn::File为根,它表示一个完整的源文件,但还有其他可能对过程宏有用的入口点,包括syn::Itemsyn::Exprsyn::Type

  • 推导 — 对于推导宏来说,syn::DeriveInput特别有用,它是推导宏的三个合法输入项之一。以下示例展示了在一个可以推导用户定义特质实现的库中使用此类型。

  • 解析 — Syn中的解析围绕解析函数构建,这些函数的签名是fn(ParseStream) -> Result<T>。Syn定义的每个语法树节点都可以单独解析,可以用作自定义语法的构建块,或者你可以想象出一个全新的语法,而不涉及我们的任何语法树类型。

  • 位置信息 — Syn解析的每个令牌都与一个Span相关联,该Span跟踪行和列信息,直到该令牌的源。这些跨度允许过程宏显示指向用户代码中所有正确位置的详细错误消息。以下是一个示例。

  • 功能标志 — 功能性被积极限制,因此您的过程宏只启用它们需要的功能,而不会在编译时间上为所有其他功能付费。

版本要求:Syn支持rustc 1.61及以上版本。

发布说明


资源

了解过程宏的最佳方式是编写一些。考虑通过这个过程宏研讨会熟悉不同类型的过程宏。研讨会在您完成每个项目时,都包含了与Syn文档相关的链接。


derive宏示例

使用Syn的规范derive宏看起来像这样。我们编写一个带有proc_macro_derive属性和要派生的特例名称的普通Rust函数。每当该派生在用户的代码中出现时,Rust编译器都会将它们的数据结构作为令牌传递给我们的宏。我们可以执行任意Rust代码来确定如何处理这些令牌,然后将一些令牌返回给编译器以编译到用户的crate中。

[dependencies]
syn = "2.0"
quote = "1.0"

[lib]
proc-macro = true
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(MyMacro)]
pub fn my_macro(input: TokenStream) -> TokenStream {
    // Parse the input tokens into a syntax tree
    let input = parse_macro_input!(input as DeriveInput);

    // Build the output, possibly using quasi-quotation
    let expanded = quote! {
        // ...
    };

    // Hand the output tokens back to the compiler
    TokenStream::from(expanded)
}

heapsize示例目录展示了derive宏的完整工作实现。该示例派生了HeapSize特例,该特例计算一个值拥有的堆内存数量的估计值。

pub trait HeapSize {
    /// Total number of bytes of heap memory owned by `self`.
    fn heap_size_of_children(&self) -> usize;
}

derive宏允许用户在程序中的数据结构上编写#[derive(HeapSize)]

#[derive(HeapSize)]
struct Demo<'a, T: ?Sized> {
    a: Box<T>,
    b: u8,
    c: &'a str,
    d: String,
}

跨度与错误报告

基于令牌的过程宏API提供了对编译器错误消息在用户代码中显示位置的良好控制。考虑如果用户的字段类型没有实现HeapSize时用户看到的错误。

#[derive(HeapSize)]
struct Broken {
    ok: String,
    bad: std::thread::Thread,
}

通过跟踪跨度信息,直到过程宏的展开,如heapsize示例所示,Syn中的基于令牌的宏能够触发直接指出问题来源的错误。

error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied
 --> src/main.rs:7:5
  |
7 |     bad: std::thread::Thread,
  |     ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HeapSize` is not implemented for `std::thread::Thread`

解析自定义语法

lazy-static示例目录展示了使用Syn的解析API解析输入令牌的functionlike!(...)过程宏的实现。

该示例重新实现了crates.io上的流行crate lazy_static,作为一个过程宏。

lazy_static! {
    static ref USERNAME: Regex = Regex::new("^[a-z0-9_-]{3,16}$").unwrap();
}

该实现展示了如何在宏输入上触发自定义警告和错误消息。

warning: come on, pick a more creative name
  --> src/main.rs:10:16
   |
10 |     static ref FOO: String = "lazy_static".to_owned();
   |                ^^^

测试

在测试宏时,我们不仅关心宏是否能够成功使用,还关心当宏接收到无效输入时,它是否能生成尽可能有帮助的错误信息。考虑使用 trybuild 库来编写测试,以检测由你的宏发出的错误或在宏误用时由 Rust 编译器检测到的错误。这些测试有助于避免后续重构中由于错误不再触发或比以前不那么有用而导致的回归。


调试

在开发过程宏时,查看生成的代码可能很有帮助。使用 cargo rustc -- -Zunstable-options --pretty=expandedcargo expand 子命令。

要显示使用你的过程宏的某些库的展开代码,在该库中运行 cargo expand。要显示某个测试用例的展开代码,运行 cargo expand --test the_test_case,其中最后一个参数是测试文件名,不包括 .rs 扩展名。

Brandon W Maister 的这篇文档详细讨论了调试: 调试 Rust 的新 Custom Derive 系统


可选功能

Syn 在许多功能后面都放置了可选功能,以优化最常见的用例的编译时间。以下功能可用。

  • derive (默认启用) — 表示 derive 宏可能的输入数据结构,包括结构体、枚举和类型。
  • full — 表示所有有效 Rust 源代码语法树的数结构,包括项和表达式。
  • parsing (默认启用) — 将输入标记解析为所选类型的语法树节点的能力。
  • printing (默认启用) — 将语法树节点打印为 Rust 源代码标记的能力。
  • visit — 遍历语法树的特质。
  • visit-mut — 遍历和就地修改语法树的特质。
  • fold — 转换自有语法树的特质。
  • clone-impls (默认启用) — 所有语法树类型的 Clone 实现。
  • extra-traits — 所有语法树类型的 Debug、Eq、PartialEq、Hash 实现。
  • proc-macro (默认启用) — 对 rustc 工具链中的动态库 libproc_macro 的运行时依赖。

过程宏包装器

Syn 在 crates.io 中的 proc-macro2 库提供的标记表示上操作,而不是直接使用编译器内置的过程宏库。这使得使用 Syn 的代码可以在过程宏之外的环境中执行,例如在单元测试或 build.rs 中,并且我们避免了过程宏与非宏用例不兼容的生态系统。

通常,所有代码都应针对 proc-macro2 而不是 proc-macro 编写。唯一的例外是过程宏入口点的签名,语言要求使用 proc_macro::TokenStream

当过程宏处于活动状态时,proc-macro2 crate 将自动检测并使用编译器的数据结构。


许可证

根据您的选择,您可以选择以下任一许可证:[Apache License, Version 2.0MIT许可证
除非您明确声明,否则根据Apache-2.0许可证定义,您有意提交以包含在此crate中的任何贡献,都将按照上述双重许可,不附加任何额外条款或条件。

依赖项