0

I'm writing a parser using nom library in rust. When using tuple I encounter a problem. This snippet works fine:

use std::str;

use nom::bytes::complete::take_while1;
use nom::bytes::complete::{tag, take_while};
use nom::character::complete::{char, multispace0};
use nom::sequence::tuple;
use nom::IResult;


fn identifier(input: &str) -> IResult<&str, &str> {
  take_while1(is_ascii_alphanumeric)(input)
}

fn parameter_separator(input: &str) -> IResult<&str, &str> {
  let my_parser = tuple((multispace0, char(','), identifier));
  let (input, _) = multispace0(input)?;
  let (input, _) = char(',')(input)?;
  let (input, _) = multispace0(input)?;

  Ok((input, ""))
}

But when replacing the identifier parser with multispace0 parser the compiler ask me to type annotate the my_parser.


fn parameter_separator(input: &str) -> IResult<&str, &str> {
  let parser = tuple((multispace0, char(','), multispace0));
  let (input, _) = multispace0(input)?;
  let (input, _) = char(',')(input)?;
  let (input, _) = multispace0(input)?;

  Ok((input, ""))
}
49  |   let parser = tuple((multispace0, char(','), multispace0));
    |       ------   ^^^^^ cannot infer type for type parameter `E` declared on the function `tuple`
    |       |
    |       consider giving `parser` a type

What is the difference? Why the second raises error?

Amir reza Riahi
  • 1,540
  • 2
  • 8
  • 34
  • your function has concrete types, but [`multispace0`](https://docs.rs/nom/latest/nom/character/complete/fn.multispace0.html) is generic, when you changed to use it the compiler was'nt able to infer the type for `multispace0` – Bamontan Aug 21 '22 at 17:37
  • @Bamontan what about the first `multispace0` used in the `tuple`? – Amir reza Riahi Aug 21 '22 at 17:39
  • yes that's the one i'm talking about, when do you use `my_parser` ? – Bamontan Aug 21 '22 at 17:45
  • @Bamontan I didn't, but using `let _ = tuple((multispace0, char(','), multispace0));` raises error as well. – Amir reza Riahi Aug 21 '22 at 17:50
  • yes, that's because there is no way for the compiler to infer the type on `multispace0`, so he is asking you to give it to it – Bamontan Aug 21 '22 at 17:51
  • `cannot infer type for type parameter E declared on the function tuple`, as `multispace0`is generic, if there is no way for the compiler to infer the type, that's what he is complaining about. – Bamontan Aug 21 '22 at 17:52
  • @Bamontan, Okay but why this works? ``` let my_parser = tuple((multispace0, char(','), identifier)); ``` – Amir reza Riahi Aug 21 '22 at 17:53
  • because `identifier` is not generic, it's already a concrete type, so there is no type inference needed. – Bamontan Aug 21 '22 at 17:54
  • @Bamontan but still I've used the `multispace0` once before, why the first time doesn't cause problem? – Amir reza Riahi Aug 21 '22 at 17:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/247428/discussion-between-bamontan-and-amir-reza-riahi). – Bamontan Aug 21 '22 at 17:55

1 Answers1

2

tuple has a generic parameter E which must be an instance of ParserError<I>.

In your second function, the compiler cannot infer the type of tuple's E parameter from the context. In tuple((multispace0, char(','), multispace0))(input), none of the arguments passed to tuple gives sufficient information of what E might be. Therefore, you are seeing the following error:

pub fn tuple<I, O, E: ParseError<I>, List: Tuple<I, O, E>>(
                      ^^^^^^^^^^^^^ required by this bound in `tuple`
  

Your first example worked because identifier has the type IResult<&str, &str> which itself expands to Result<(&str, &str), nom::Err<nom::error::Error<&str>>>.

This allows the compiler to instantiate E to nom::Err<nom::error::Error<&str>> in the tuple() call.

Thus, if you define an alias for multispace0 that does not leave E unspecified, the code would compile correctly:

fn narrowed_multispace0(input: &str) -> Result<(&str, &str), nom::Err<nom::error::Error<&str>>> {
  multispace0(input)
}

let _ = tuple((narrowed_multispace0, char(','), multispace0))(input);

Rust implements the Hindley-Milner type system which performs bidirectional type inference. This allows E to be inferred even from the function's result type as in the following example:

fn parameter_separator(input: &str) -> IResult<&str, &str> {
  map(  // To map (&str, &str, &str)` onto `&str`
    tuple((multispace0, char(','), multispace0)),
    |_| "test"
  )(input)
}
Tim Nieradzik
  • 112
  • 1
  • 6