Search code examples
rustrust-proc-macros

How does syn::parse::ParseBuffer::peek() accept a type as an argument?


How are we able to pass type as an argument instead of turbofish to syn::parse::ParseBuffer::peek?

Methods are generally called like input.parse::<Token![;]>(). We pass types as generics but peek expects them as input.peek(Token![;]).

I wanted to know because if I create a type alias to Token![;] like type TokenAlias = Token![;] and use it like input.peek(TokenAlias), I get the error:

expected value, found type alias `TokenAlias` can't use a type alias as a constructor

which would have made sense if Token![;] was also not allowed.

Why do I get this error when I use type alias?

Code with TokenAlias:

// COMPILE ERROR: expected value, found type alias `TokenAlias` can't use a type alias as a constructor

type TokenAlias = Token![;];

impl Parse for PathSegments {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut segments = Punctuated::new();

        let first = parse_until(input, TokenAlias)?;
        segments.push_value(syn::parse2(first)?);

        while input.peek(TokenAlias) {
            segments.push_punct(input.parse()?);

            let next = parse_until(input, TokenAlias)?;
            segments.push_value(syn::parse2(next)?);
        }

        Ok(PathSegments { segments })
    }
}

Playground

Code with Token![;]:

// BUILDS SUCCESSFULLY

impl Parse for PathSegments {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut segments = Punctuated::new();

        let first = parse_until(input, Token![;])?;
        segments.push_value(syn::parse2(first)?);

        while input.peek(Token![;]) {
            segments.push_punct(input.parse()?);

            let next = parse_until(input, Token![;])?;
            segments.push_value(syn::parse2(next)?);
        }

        Ok(PathSegments { segments })
    }
}

Playground

Following is the source code of peek from syn:

pub fn peek<T: Peek>(&self, token: T) -> bool {
    let _ = token;
    T::Token::peek(self.cursor())
}

Source code


Solution

  • The peek function accepts a token value, not a type.

    The reason it accepts Token[;] is that it expands to an identifier that is the name of a type, but also of a global function hidden from docs! Your type alias refers to the type, and attempting to construct a value using constructor syntax for unit-like structs makes the compiler complain that such syntax doesn't work for type aliases. (And even if it did, Semi is not a unit-like struct, so it wouldn't work for that type in particular.)