liveBook · Manning

MetaList

As explained in Write Powerful Rust Macros

MetaList is a syn syntax structure representing list-form Rust metadata such as an attribute path followed by nested arguments.

MetaList is the syn structure for list-form Rust metadata: an attribute path followed by parenthesized arguments, such as #[exclude(fourth, third)]. It is the focused choice when a procedural macro needs the list contents rather than a path or name-value form. section 8.7 section 8.1.2

A practical MetaList parsing flow is:

  • Parse the attribute input as MetaList, then inspect its path to confirm the expected attribute name, such as exclude. section 8.7
  • Parse the list tokens into a suitable type, such as Punctuated<Ident, Token![,]> for bare field names. section 8.7
  • Use LitStr instead when the syntax contains quoted values, such as exclude("fourth", "third"), and convert each literal with value. section 8.7
  • Prefer require_list for simple validation, or use syn::meta::parser when custom nested parsing and clearer errors are useful. section 8.1.2 section 8.7

Selecting excluded fields

Given #[public(exclude(fourth, third))], a custom ExcludedFields parser stores the identifiers as strings. The procedural macro then checks each struct field with matches_ident: excluded fields retain their existing visibility, while other fields become public. section 8.7

Listing 8.37 ExcludedFields implementation for syn major version 2
const EXCLUDE_ATTRIBUTE_NAME: &str = "exclude";

struct ExcludedFields {
    fields: Vec<String>
}

impl ExcludedFields {
    fn matches_ident(&self, name: &Option<Ident>) -> bool {
        name.as_ref().map(|n| n.to_string())
            .map(|n| self.fields.iter().any(|f| *f == n))
            .unwrap_or_else(|| false)
    }
}

impl Parse for ExcludedFields {
    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
        match input.parse::<MetaList>() {       #1
            Ok(meta_list) => {
                if meta_list.path
                    .segments
                    .iter()
                    .find(|s| s.ident == EXCLUDE_ATTRIBUTE_NAME)
                    .is_some()                  #2
                {
                    let parser = Punctuated::<Ident, Token![,]>
::parse_terminated;
                    let identifiers = parser.parse(
                        meta_list.clone().tokens.into()
                    ).unwrap();
                    let fields = identifiers.iter()
                        .map(|v| v.to_string())
                        .collect();                #3
                    Ok(ExcludedFields { fields })
                } else {
                    Ok(ExcludedFields { fields: vec![] })
                }
            }
            Err(_) => Ok(
                ExcludedFields { fields: vec![] } #4
            )                
        }
    }
}

Inspect the syn v2 implementation that parses MetaList, verifies the exclude path, and begins extracting comma-separated identifiers. section 8.7

How Write Powerful Rust Macros covers it

How deep it goes
Explains structure selection, path checking, token parsing, quoted alternatives, and syn v1 and v2 implementation differences.
Examples
Uses #[public(exclude(fourth, third))], comma-separated identifiers, quoted LitStr values, and an ExcludedFields parser.
What it assumes
Assumes Rust procedural-macro and syn familiarity while distinguishing MetaList from Meta and explaining the relevant parsing path.
What you can do after
Build custom procedural-macro attribute parsers that validate list names and extract identifier or string arguments.
How it connects
Connects attributes with Meta, TokenStream, procedural macros, Punctuated parsing, and custom Parse implementations.

This explainer was generated by Manning's knowledge graph from the text of Write Powerful Rust Macros. The section links point to the parts of the book it draws on.