nom
nom copied to clipboard
Whitespace trimming recipe too strict
- Rust version: 1.59.0
- nom version: 7.1.0
Currently, the whitespace trimming in the recipes documentation requires an inner function Fn where it should probably only require FnMut. This artificially restricts many functions from being used, including most in the combinator, multi, and sequence modules.
use nom::{
IResult,
error::ParseError,
combinator::value,
sequence::delimited,
character::complete::multispace0,
bytes::complete::tag,
};
fn ws<'a, F: 'a, O, E: ParseError<&'a str>>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
where
F: Fn(&'a str) -> IResult<&'a str, O, E>,
{
delimited(
multispace0,
inner,
multispace0
)
}
fn example_working(input: &str) -> IResult<&str, &str> {
ws(tag("test"))(input)
}
fn example_failing(input: &str) -> IResult<&str, i32> {
ws(value(42, tag("test")))(input)
}
Here, example_failing fails to compile:
error[E0277]: expected a `Fn<(&str,)>` closure, found `impl FnMut(&str)-> Result<(&str, {integer}), nom::Err<_>>`
One solution is to simply wrap ws around tag instead of value (e.g. value(42, ws(tag("test")))), which is reasonable in this case but very tedious in others, for example 10 variants of alt.
This would be solved by changing the type generic F from Fn to FnMut. Given that FnMut is the type that delimiter accepts anyway, I see no reason why Fn should be used instead.
Would be fixed with pull request #1514.
I ran into a similar issue with the ws recipe today. I wanted to use the ws combinator with a parser that holds a reference to some local state of the enclosing parser. This is not possible with the current recipe, as the closure F is bounded by the lifetime 'a of the input.
Example that doesn't compile:
fn my_parser<'s, 'i>(some_state: &'s i32) -> impl Fn(&'i str) -> IResult<&'i str, ()> + 's {
move |i: &str| {
// the reference to some state is needed in the parser. The actual
// type is not copy
let state: &i32 = some_state;
Ok((i, ()))
}
}
fn my_other_parser(i: &str) -> IResult<&str, ()> {
let local_state = 5;
let (i, parsed) = ws(my_parser(&local_state))(i)?;
Ok((i, parsed))
}
I believe it should be totally fine to remove the F: 'a bound. If I remove the bound, the above example works ([Working Playground]). If I'm not mistaken, removing the bound should allow strictly more parsers to be passed to the combinator.
Good point.