In this lesson, you will learn how to inject different code paths into your procedural macros based on the structural inspection of the target type.
When writing a derive macro, you aren't just generating static strings; you are inspecting the Abstract Syntax Tree (AST). Often, you want your generated implementation to behave differently depending on the data the struct contains. For example, you might want to skip Option<T> fields during serialization or generate a specific validation routine only if a field has a custom attribute.
Inspecting the AST
When you use syn to parse your input, you receive a Data enum. This enum allows you to branch your logic:
match input.data {
Data::Struct(data_struct) => {
// Logic for structs: iterate over fields
let fields = data_struct.fields;
// Generate code based on field presence
}
Data::Enum(data_enum) => {
// Logic for enums: iterate over variants
}
Data::Union(_) => panic!("Unions not supported"),
}
Implementing Conditional Generation
Within the Data::Struct branch, you can filter fields to decide what to emit. Suppose you are writing a derive(Summarize) macro. You might want to ignore fields marked with #[ignore_me].
Inside your quote! block, you can use syn's ability to iterate over fields and apply logic:
let field_logic = fields.iter().filter_map(|f| {
// Check for custom attributes
let is_ignored = f.attrs.iter().any(|a| a.path.is_ident("ignore_me"));
if is_ignored {
None // This field will not be included in the generation
} else {
let name = &f.ident;
Some(quote! { println!("{}: {:?}", stringify!(#name), self.#name); })
}
});
let expanded = quote! {
impl Summarize for #name {
fn summarize(&self) {
#(#field_logic)*
}
}
};
By leveraging filter_map or conditional quote! blocks, you decouple the macro's output from the exact definition of the target struct. The macro becomes an intelligent engine that transforms the structure rather than a rigid template. This allows users of your macro to influence the generated code through simple field-level annotations, creating a highly flexible API.
Key Takeaway: Use the syn AST structure to branch your macro logic, allowing the generated code to react dynamically to the fields and attributes present in the target type.