我正在rust中使用nom
库编写一个解析器。在使用tuple
时,我遇到了一个问题。这个片段运行良好:
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, ""))
}
但是当用multispace0
解析器替换identifier
解析器时,编译器要求我键入注释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
有什么区别?为什么第二个会引发错误?
tuple
有一个泛型参数E
,它必须是ParserError<I>
的实例。
在第二个函数中,编译器无法从上下文推断tuple
的E
参数的类型。在tuple((multispace0, char(','), multispace0))(input)
中,传递给tuple
的参数都没有提供E
可能是什么的足够信息。因此,您会看到以下错误:
pub fn tuple<I, O, E: ParseError<I>, List: Tuple<I, O, E>>(
^^^^^^^^^^^^^ required by this bound in `tuple`
您的第一个示例之所以有效,是因为identifier
的类型IResult<&str, &str>
本身扩展为Result<(&str, &str), nom::Err<nom::error::Error<&str>>>
。
这允许编译器在tuple()
调用中实例化E
到nom::Err<nom::error::Error<&str>>
。
因此,如果您为multispace0
定义一个别名,而不指定E
,则代码将正确编译:
fn narrowed_multispace0(input: &str) -> Result<(&str, &str), nom::Err<nom::error::Error<&str>>> {
multispace0(input)
}
let _ = tuple((narrowed_multispace0, char(','), multispace0))(input);
Rust实现了执行双向类型推理的Hindley-Milner类型系统。这使得即使从函数的结果类型也可以推断出E
,如下例所示:
fn parameter_separator(input: &str) -> IResult<&str, &str> {
map( // To map (&str, &str, &str)` onto `&str`
tuple((multispace0, char(','), multispace0)),
|_| "test"
)(input)
}