0

我正在编写一个过程宏来将枚举的变体转换为单独的结构并为该结构实现一些特征。

这适用于单元和未命名的变体,但具有命名数据的变体将导致它静默失败:)。

这是一个示例 proc_macro 定义:

extern crate proc_macro;
use quote::ToTokens;

use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DataEnum, DeriveInput};

#[proc_macro_derive(MyProcMacro)]
pub fn derive_my_proc_macro(input: TokenStream) -> TokenStream {
    let ast: DeriveInput = syn::parse(input).unwrap();

    // Error out if we're not annotating an enum
    let data: DataEnum = match ast.data {
        Data::Enum(d) => d,
        _ => panic!("My structs can only be derived for enums"),
    };
    let variants = data.variants.iter();
    let variant_structs = variants.map(|v| {
        let var_id = &v.ident;
        let fields = v.fields.clone().into_token_stream();
        quote! {
            pub struct #var_id #fields;
            /* Implement traits for the new struct and stuff */
        }
    });
    let gen = quote! {
        #(#variant_structs)*
    };
    gen.into()
}

当我在这段代码上运行它时:

#[derive(MyProcMacro)]
enum AllTypesOfVariants {
    Unit,
    OneUnNamed(bool),
    MultiUnNamed(bool, bool, bool),
    Named { _thing: bool },
    MultiNamed { _thing: bool, _thing2: bool },
}

我得到这个扩展代码(通过cargo expand):


pub struct Unit;
pub struct OneUnNamed(bool);
pub struct MultiUnNamed(bool, bool, bool);
pub struct Named {
    _thing: bool,
}

然而,预期的结果是:

pub struct Unit;
pub struct OneUnNamed(bool);
pub struct MultiUnNamed(bool, bool, bool);
pub struct Named {
    _thing: bool,
}
pub struct MultiNamed {
    _thing: bool,
    _thing2: bool
}
4

1 回答 1

0

问题出在quote!().

具有未命名字段的结构应以分号结尾:

pub struct MultiUnNamed(bool, bool, bool);

但是具有命名字段的结构不应该:

pub struct MultiNamed {
    _thing: bool,
    _thing2: bool
}

问题通过更换解决:

quote! {
    pub struct #var_id #fields;
}

match &v.fields {
    Fields::Named(_) => {
        quote! {
            pub struct #var_id #fields
        }
    },
    _ => {
        quote! {
            pub struct #var_id #fields;
        }
    }
}

(也必须导入syn::Fields

于 2019-07-31T21:37:54.353 回答