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?
CodePudding user response:
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)
}