rustparser-combinatorsnom

Function `impl Parser` trait no working with `alt` combinator in nom


I use the alt function of nom to build a parser for bot commands, namely parse_command in the example.

# src/main.rs
use nom::{
  IResult,
  bytes::complete::{tag},
  branch::alt,
  combinator::map,
};

#[derive(Debug, PartialEq)]
pub enum Command {
    Foo(),
    Bar(),
}

pub fn parse_command(input: &str) -> IResult<&str, Command> {
    alt((
        parse_foo,
        parse_bar,
    ))(input)
}

fn parse_foo(input: &str) -> IResult<&str, Command> {
    map(tag("!bar"), |_| Command::Bar())(input)
}

fn parse_bar(input: &str) -> IResult<&str, Command> {
    map(tag("!foo"), |_| Command::Foo())(input)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_bar() {
        let s = "!bar";
        let (_, result) = parse_command(s).unwrap();
        assert_eq!(result, Command::Bar());
    }

    #[test]
    fn test_parse_foo() {
        let s = "!foo";
        let (_, result) = parse_command(s).unwrap();
        assert_eq!(result, Command::Foo());
    }
}

This works fine. However, reading examples, I was under the impression that I could update the signature of the parse_bar and parse_foo functions like so:

use nom::{
    Parser,
    error::Error,
};

fn parse_foo<'a>() -> impl Parser<&'a str, Command, Error<&'a str>> {
    map(tag("!bar"), |_| Command::Bar())
}

fn parse_bar<'a>() -> impl Parser<&'a str, Command, Error<&'a str>> {
    map(tag("!foo"), |_| Command::Foo())
}

But then, the compiler screams at me:

error[E0593]: function is expected to take 1 argument, but it takes 0 arguments
  --> src/main.rs:18:9
   |
17 |     alt((
   |     --- required by a bound introduced by this call
18 |         parse_foo,
   |         ^^^^^^^^^ expected function that takes 1 argument
...
24 | fn parse_foo<'a>() -> impl Parser<&'a str, Command, Error<&'a str>> {
   | ------------------------------------------------------------------- takes 0 arguments
   |
   = note: required for `fn() -> impl Parser<&str, Command, nom::error::Error<&str>> {parse_foo::<'_>}` to implement `Parser<_, _, _>`
   = note: required for `(fn() -> impl Parser<&str, Command, nom::error::Error<&str>> {parse_foo::<'_>}, fn() -> impl Parser<&str, Command, nom::error::Error<&str>> {parse_bar::<'_>})` to implement `nom::branch::Alt<_, _, _>`
note: required by a bound in `alt`
  --> /home/pamplemousse/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/branch/mod.rs:47:49
   |
47 | pub fn alt<I: Clone, O, E: ParseError<I>, List: Alt<I, O, E>>(
   |                                                 ^^^^^^^^^^^^ required by this bound in `alt`

[... and same error for `parse_bar`...]

Are my expectations about using the Parser trait to implement such functions wrong? If not, what am I missing/misunderstanding so that my updated code works?


Solution

  • Your second example, where it fails, doesn't show the call to parse_command. If it is exactly the same as the first call which works, then the problem is that you are not calling the functions to return the actual Parser implementations, and instead you are passing fn's where it expects Parser impls.

    So the fix should be:

    pub fn parse_command(input: &str) -> IResult<&str, Command> {
        alt((
            parse_foo(), 
            parse_bar(),
        ))(input)
    }
    

    To be clear, the difference is parse_foo() vs parse_foo, similarly for parse_bar.