proc_macro2
proc_macro2
是对 proc_macro
的包装,因为后者只能用在 proc-macro crate 中。
这两个的库的大多类型都相互实现了 From
,所以可以相互转化。但出于习惯1,我们常常使用 from
方法,而不使用
into
方法(当然,你要是想使用 into
方法,也没有人会阻止你)。
1
仅限于过程宏范畴,因为这一说法来自于我对 dtolnay/syn#35b498 的观察。
我们可以在内部使用 proc_macro2
的类型,而在最后的过程宏函数中把
proc_macro2::TokenStream
转化成 proc_macro::TokenStream
。
// src: https://github.com/zjp-CN/proc-macro-workshop/blob/master/bitfield/impl/src/lib.rs
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn bitfield(_: TokenStream, input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::Item);
TokenStream::from(bit::expand(input))
}
#[proc_macro]
pub fn gen(_: TokenStream) -> TokenStream {
TokenStream::from(gen::generate())
}
#[proc_macro_derive(BitfieldSpecifier)]
pub fn derive_bitfield_specifier(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::ItemEnum);
TokenStream::from(spe::derive_bitfield_specifier_for_enum(input))
}
接下来讨论 TokenStream
和 TokenTree
。
TokenStream
TokenStream
的 inherent 方法很少,只有 fn new() -> Self
和
fn is_empty(&self) -> bool
。更重要的是其 trait 实现:
IntoIterator
:从TokenStream
到TokenTree
只需要.into_iter()
FromIterator<TokenStream>
:把多个TokenStream
汇聚成一个TokenStream
FromIterator<TokenTree>
:把多个TokenTree
汇聚成一个TokenStream
Extend<TokenStream>
:把多个TokenStream
添加进来Extend<TokenTree>
:把多个TokenTree
添加进来
构造 TokenStream
并不难,最常用 quote::quote!
。
借助 Iterator
相关的泛型实现,不难理解下面的代码 —— 把多个 TokenStream
汇总成一个 TokenStream
:
use proc_macro2::TokenStream;
fn main() {
{
// `Extend<TokenStream>`
let mut ts = TokenStream::new();
ts.extend(iter());
assert_eq!(count(ts), TS);
}
{
// `FromIterator<TokenStream>` + impl<I: Iterator> IntoIterator for I
let ts = TokenStream::from_iter(iter());
assert_eq!(count(ts), TS);
}
{
// `FromIterator<TokenStream>` + `Iterator::collect()`
let ts: TokenStream = iter().collect();
assert_eq!(count(ts), TS);
}
{
// quote! 的反复插值
let iter = iter();
assert_eq!(count(quote::quote! {#(#iter)*}), TS);
}
}
const N: usize = 10;
const TT: usize = 7;
const TS: usize = N * TT;
fn f(i: usize) -> TokenStream {
// 这里有 7 个 TokenTree
quote::quote! { const _: usize = #i; }
}
fn iter() -> impl Iterator<Item = TokenStream> { (0usize..N).map(f) }
fn count(ts: TokenStream) -> usize { ts.into_iter().count() }
TokenTree
TokenTree
是一个枚举体,它描述一个标记的类别:
pub enum TokenTree {
Group(Group),
Ident(Ident),
Punct(Punct),
Literal(Literal),
}
类型 | 含义 |
---|---|
Group | 由 {} 、 () 、 [] 等分隔出来的标记(包括两侧的括号分隔符) |
Ident | 一个标识符,比如 ABC 、 _ 、 let |
Punct | 一个标点,比如 + 、 , 、 $ |
Literal | 一个字面值,比如字符 'a' 、字符串 "hello" 、数字 2.3f64 |
你很少自己通过 TokenTree
解析标记,因为 syn
提供大量基础而通用的节点类型,它们都实现了 Parse
trait。
一个很好的必须使用 TokenTree
的案例是 proc-macro-workshop 的
Seq
,你可以参考我的解答。