场景 开发过程宏时经常需要处理结构体或枚举体上的属性参数,如下 Command 结构体的 args 字段有属性 each = "arg", #[derive(Builder)] pub struct Command { executable: String, #[builder(each = "arg")] args: Vec<String>, #[builder(each = "env")] env: Vec<String>, current_dir: Option<String>, } AST 对应的语法树结构: // Command 语法树 DeriveInput { // Command 结构体属性 当前没有 attrs: [], vis: Visibility::Public( Pub, ), // 结构体名称 ident: Ident { ident: "Command", span: #0 bytes(206..213), }, generics: Generics { lt_token: None, params: [], gt_token: None, where_clause: None, }, data: Data::Struct { struct_token: Struct, // Fields::Named字段集合 fields: Fields::Named { brace_token: Brace, named: [ // executable 字段 Field { attrs: [], vis: Visibility::Inherited, mutability: FieldMutability::None, // 字段名 executable ident: Some( Ident { ident: "executable", span: #0 bytes(220..230), }, ), colon_token: Some( Colon, ), // 字段类型 String ty: Type::Path { qself: None, path: Path { leading_colon: None, segments: [ PathSegment { ident: Ident { ident: "String", span: #0 bytes(232..238), }, arguments: PathArguments::None, }, ], }, }, }, Comma, // args 字段 Field { // 字段指定的属性参数集合 attrs: [ Attribute { pound_token: Pound, style: AttrStyle::Outer, bracket_token: Bracket, meta: Meta::List { // 属性名称 builder path: Path { leading_colon: None, segments: [ PathSegment { ident: Ident { ident: "builder", span: #0 bytes(246..253), }, arguments: PathArguments::None, }, ], }, delimiter: MacroDelimiter::Paren( Paren, ), tokens: TokenStream [ // 第一个属性 key = each Ident { ident: "each", span: #0 bytes(254..258), }, Punct { ch: '=', spacing: Alone, span: #0 bytes(259..260), }, // 第一个属性 value = arg Literal { kind: Str, symbol: "arg", suffix: None, span: #0 bytes(261..266), }, ], }, }, ], vis: Visibility::Inherited, mutability: FieldMutability::None, // 字段名 args ident: Some( Ident { ident: "args", span: #0 bytes(273..277), }, ), colon_token: Some( Colon, ), // 字段类型 Vec<String> ty: Type::Path { qself: None, path: Path { leading_colon: None, segments: [ PathSegment { ident: Ident { ident: "Vec", span: #0 bytes(279..282), }, arguments: PathArguments::AngleBracketed { colon2_token: None, lt_token: Lt, args: [ GenericArgument::Type( Type::Path { qself: None, path: Path { leading_colon: None, segments: [ PathSegment { ident: Ident { ident: "String", span: #0 bytes(283..289), }, arguments: PathArguments::None, }, ], }, }, ), ], gt_token: Gt, }, }, ], }, }, }, // 其它字段省略 宏入口 use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(Builder, attributes(builder))] pub fn builder_derive(input: TokenStream) -> TokenSteam { let input = parse_macro_input(input as DeriveInput); // 从 data中获取 fields 字段数据集合 let fields = ...; let setter_fns = fields.iter().map(|f| { // 无属性参数时功能处理 if f.attrs.is_empty() { return quote!{}; } // 遍历属性参数 for attr in f.attrs.iter() { // parse_builder_attr 解析当前的属性数据 let arguments = parse_builder_attr(attr); // 根据属性参数再处理其它处理 } }); } 属性解析实现 /// builder 属性参数数据类型 struct BuilderAttribute { /// 属性Key pub key : String, /// 属性值 pub value: Option<String>, /// 属性meta path, 用于错误提示位置显示 /// 类似: /// --> main.rs:14:7 /// | /// 14| #[builder(each = "arg")] /// | ^^^^^^^^^^^^^^^^^^^^ pub meta: Meta, } /// 属性参数解析 /// #[builder(each = "env")] /// 转为 /// [BuilderAttribute { key: "each", value: Some("env"), meta: syn::Meta }] fn parse_builder_attr(attr: &syn::Attribute) -> Vec<BuilderAttribute> { let mut arguments = Vec::new(); if attr.path().is_ident("builder") { // parse_args_with 参考: // https://docs.rs/syn/2.0.48/syn/struct.Attribute.html#alternatives // nested 类型 Vec<Meta::NameValue> if let Ok(nested) = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated) { for meta in &nested { if let Some(ident) = meta.path().get_ident() { let key = ident.to_string(); let mut value: Option<String> = None; if let Meta::NameValue(name_value) = meta { if let Expr::Lit(ref expr_lit) = name_value.value { value = match expr_lit.lit { Lit::Str(ref lit_str) => Some(lit_str.value()), Lit::Bool(ref lit_bool) => Some(lit_bool.value.to_string()), Lit::Int(ref lit_int) => Some(lit_int.base10_digits().to_owned()), _ => None, } } }; arguments.push(BuilderAttribute { key, value, meta: attr.meta.clone(), }); } } } } arguments } 解析结果 [ BuilderAttribute { // 属性键 key: "each", // 属性值 value: Some( "arg", ), // Meta meta: Meta::List { path: Path { leading_colon: None, segments: [ PathSegment { ident: Ident { ident: "builder", span: #0 bytes(246..253), }, arguments: PathArguments::None, }, ], }, delimiter: MacroDelimiter::Paren( Paren, ), tokens: TokenStream [ Ident { ident: "each", span: #0 bytes(254..258), }, Punct { ch: '=', spacing: Alone, span: #0 bytes(259..260), }, Literal { kind: Str, symbol: "arg", suffix: None, span: #0 bytes(261..266), }, ], }, }, ]