1

I'm looking at the nom crate for rust, which contains lots of functions to parse bytes/characters.

Many of the functions, such as tag(), seen below, process input that's provided not as a parameter to the function, but that appears instead in a second set of parentheses, following what I would call the parameters. If, in examples, one looks for a needle in a haystack, then the tag() function uses a parameter of its own, which is how one specifies the needle, but the haystack is specified separately, after the parameter parentheses, inside parentheses of its own (perhaps because it's a single value tuple?).

use nom::bytes::complete::tag;

fn parser(s: &str) -> IResult<&str, &str> {
  tag("Hello")(s)
}

In the example above, tag()'s job is to test whether the input s starts with Hello. You can call parser, passing in "Hello everybody!, and the tag() function does indeed verify that the start of s is Hello. But how did (s) find its way into tag()?

Can someone explain this syntax to me, or show where to read about it. It works, and I can use it, but I don't understand what I'm looking at!

thanks

Sam
  • 33
  • 3

2 Answers2

2

The return value of tag() is impl Fn(Input) -> IResult<Input, Input, Error>, i.e. the function returns another function. The first set of parentheses is for calling tag(); the second set is for calling the function it returns.

This allows you to store the "parser" returned by these functions in a variable and use it multiple times. Or, put differently, instead of the function definition in your question you could also write

let parser = tag("Hello");

and then call parser the same way you would call the function.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • Perfect. Thanks. I thought the complicated function definition somehow did something to isolate one parameter, such that it would not be passed in in the usual way, but I see now, that the result is a function, and we're just calling that function. Thanks. – Sam Jan 25 '21 at 13:04
0

tag("Hello") just returns a function, which is then immediately invoked with the argument s, i.e. tag("Hello")(s). Here's a simple implementation example:

fn tag<'a>(needle: &'a str) -> impl Fn(&str) -> bool + 'a {
    move |haystack: &str| haystack.starts_with(needle)
}

fn parser(s: &str) -> bool {
    tag("Hello")(s)
}

fn main() {
    println!("{}", parser("Hello everbody!")); // true
    println!("{}", parser("Bye everybody!")); // false
}

playground

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98