liveBook · Manning

Punctuated

As explained in Write Powerful Rust Macros

Punctuated is a syn sequence structure that stores syntax elements together with their punctuation separators.

`Punctuated<T, P>` represents syntax elements together with the punctuation that separates them. It fits repeated grammar such as struct fields separated by commas or DSL properties separated by commas, while iteration yields the element type rather than the separator. section 4.4

Use it when a parser or syntax tree contains a repeated, delimited sequence:

  • Traverse named struct fields as `Punctuated<Field, Token![,]>`, then map each field’s identifier and type into generated `pub` fields. section 4.4
  • Parse parenthesized DSL properties with `Punctuated::<KeyValue, Token!(,)>::parse_terminated`, then inspect each parsed key-value pair. section 9.4.1
  • Parse a simplified field token stream as identifiers separated by `Colon`, taking the first and last parsed identifiers as name and type. section 4.6.2

Parsing comma-separated properties

After `parenthesized!(content in input)`, parse the contents as `Punctuated::<KeyValue, Token!(,)>::parse_terminated(&content)?`. `KeyValue` consumes an identifier, requires `=`, and parses a value; later logic recognizes `name`, `mem`, and `time` or reports an error. section 9.4.1

Listing 9.10 Lambda and its new Parse
// other imports
use syn::parenthesized;
use syn::punctuated::Punctuated;

impl Parse for Lambda {
    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
        let _ = input.parse::<kw::lambda>()
            .expect("we just checked for this token");
        let mut lambda_name = None;
        let mut lambda_memory = None;
        let mut lambda_timeout = None;

        let content;
        parenthesized!(content in input);     #1

        let kvs = Punctuated::<KeyValue, Token!(,)>::parse_terminated( #2
            &content
        )?;                                  
        kvs.into_iter().for_each(|kv| {
            if kv.key == "name" {
                lambda_name = Some(kv.value);   #3
            } else if kv.key == "mem" {
                lambda_memory = Some(
                    kv.value.parse().unwrap() #4
                );                             
            } else if kv.key == "time" {
                lambda_timeout = Some(
                    kv.value.parse().unwrap()
                );                              
            }
        });

        Ok(Lambda {
            name: lambda_name.ok_or(syn::Error::new( #5
                input.span(),
                "lambda needs a name"
            ))?,                            
            memory: lambda_memory,
            time: lambda_timeout,
        })
    }
}

The listing shows `Punctuated` parsing parenthesized, comma-separated `KeyValue` properties before their keys are validated and assigned. section 9.4.1

How Write Powerful Rust Macros covers it

How deep it goes
Demonstrates iteration and `parse_terminated` parsing, while noting that the simplified field parser accepts only unannotated named fields.
Examples
Uses comma-separated struct fields, parenthesized `name = value` DSL properties, and colon-separated field-name/type tokens.
What it assumes
Assumes familiarity with Rust procedural-macro parsing while incrementally showing `Parse` implementations and token consumption.
What you can do after
Apply it via `syn` to iterate syntax fields or parse delimited custom properties with punctuation validation.
How it connects
Connects `Punctuated` with `Parse`, `ParseStream`, `Token`, `Ident`, `Visibility`, and `syn::parse2`.

Related concepts

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.