4

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?

1
  • 1
    The proc-macro can't actually know what a bool is. The Type refers to syntax rules (e.g. "this is a tuple, because it looks like a tuple"), but the proc-macro can't actually do type-resolution. So by definition, if I do type Foo = bool; or struct Foo(bool), the proc-macro will only ever see a type named Foo. You could special-case for a type named bool and hope that this will resolve to core::primitive::bool. Commented Apr 1, 2021 at 14:34

2 Answers 2

6

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I like your first solution. I'm going to see if this works though: rust let bool_fields = fields.iter().filter(|field| match &field.ty { syn::Type::Path(type_path) if type_path.path.is_ident("bool") => true, _ => false, } );
Works like a charm! Thanks for pointing me in the right direction. You could use is_ident to get around cloning thought.
1

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

4 Comments

This would also match a qualified type bool::SomeOtherType.
Ah, good point. Let me see if it's fixable
Although, to be fair, it would be unlikely/weird for someone to define a module called bool.
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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.