简体   繁体   中英

Rust proc_macro_derive: How do I check if a field is of a primitive type, like boolean?

I'm trying to filter out all the fields of a struct that are of type bool . But the syn::Type enum doesn't seem to have a case for it, or I'm reading the definitions incorrectly:

pub enum Type {
    Array(TypeArray),
    BareFn(TypeBareFn),
    Group(TypeGroup),
    ImplTrait(TypeImplTrait),
    Infer(TypeInfer),
    Macro(TypeMacro),
    Never(TypeNever),
    Paren(TypeParen),
    Path(TypePath),
    Ptr(TypePtr),
    Reference(TypeReference),
    Slice(TypeSlice),
    TraitObject(TypeTraitObject),
    Tuple(TypeTuple),
    Verbatim(TokenStream),
    // some variants omitted
}

I looked trough syn::Types source, to check which variants where ommited, but that didn't bring me any further. Here's what I have until now:

#[proc_macro_derive(Creator)]
pub fn derive_creator(_item: TokenStream) -> TokenStream {
    let item = parse_macro_input!(_item as syn::DeriveInput);
    let item_ident = item.ident;

    let fields = if let syn::Data::Struct(syn::DataStruct {
        fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
        ..
    }) = item.data
    {
        named
    } else {
        panic!("You can derive Creator only on a struct!")
    };

    let bool_fields = fields.iter().filter(|field| 
        match field.ty {
            // case when field type is bool => true
            _ => false
        }
    );
    unimplemented!()
}

Am I going down the wrong path? Or is this simply not possible? Or am I missing something?

I feel like there might be a cleaner way (without having to clone and allocate a string), but in the past I've done something like:

match field.ty {
    Type::Path(type_path) if type_path.clone().into_token_stream().to_string() == "bool" => {
        true
    }
    _ => false
}

You might be able to define the bool type once and then compare it for equality:

let bool_ty = Type::Path(TypePath {
    qself: None,
    path: Path::from(Ident::new("bool", Span::call_site())),
});

if field.ty == bool_ty {
    // it's a bool
}

But I'm not sure if a difference in the span would affect equality. Span appears to not implement PartialEq , so my guess is that this is ok.*


* Edits to clarify this are welcome.

Another way:

    let bool_fields = fields.iter().filter(|field| {
        if let Type::Path(tp) = &field.ty {
            let segments = &tp.path.segments;
            // Check len to avoid "bool::SomeOtherType".
            if segments.len() == 1 {
                return segments[0].ident == "bool";
            }
        }
        false
    });

Bear in mind:

Worth pointing out that this checks for equality by name. A type alias, a single-field tuple or core::primitive::bool will not match, although they are all bool after type resolution. – user2722968

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM