rustnom

Necessary trait bounds on generic function implementing nom parser


Goal: To have the map(..) extracted into a function, so that it can be used in an alt(..) statement. There are probably some mistakes here. The code:

fn extract_unsigned_value<'a, I, K, F, P, O>(key: K, parser: P, c: F) -> impl FnMut(I) -> IResult<I, I, Error<I>>
    where F: FnMut(O),
          P: nom::Parser<&'a str, O, Error<&'a str>>,
          I: InputTake + Compare<K>,
          K: InputLength + Clone
{
    move |i: I| {
        map(preceded(
            tuple((tag(key), multispace0::<&str, Error<&str>>, char(':'), multispace0)),
            parser,
        ), c,
        )
    }
}

The compiler error:

error[E0271]: type mismatch resolving `<&str as InputIter>::Item == u8`
  --> src/main.rs:75:20
   |
75 |             tuple((tag(key), multispace0::<&str, Error<&str>>, char(':'), multispace0)),
   |                    ^^^ expected `u8`, found `char`
   |
   = note: required for `&str` to implement `Compare<K>`
note: required by a bound in `nom::bytes::complete::tag`
  --> /home/merlin/.cargo/registry/src/github.com-1ecc6299db9ec823/nom-7.1.3/src/bytes/complete.rs:36:22
   |
36 |   Input: InputTake + Compare<T>,
   |                      ^^^^^^^^^^ required by this bound in `tag`

I tried a lot in the last couple of days, event crying, did not help. I tried to start implementing this from the inside out, and got this far:

fn extract_unsigned_value<'a, T, F, P,E: ParseError<T>, O>(key: T, parser: P) -> impl FnMut(T) -> IResult<T, T, Error<T>>
    where
        T: InputTake + Compare<T> + Clone + nom::InputTakeAtPosition + InputLength + nom::Slice<RangeFrom<usize>> + InputIter,
        <T as nom::InputTakeAtPosition>::Item: AsChar + Clone,
        <T as InputIter>::Item: AsChar,
        P: nom::Parser<T, T, E>,
{
    move |input: T| {
        preceded(
            tuple((tag(key.clone()), multispace0::<T, Error<T>>, char(':'), multispace0)),
            parser,
        )
            (input)
    }
}

which unhelpful throws:

error[E0277]: expected a `FnMut<(T,)>` closure, found `P`
   --> src/main.rs:102:13
    |
100 |         preceded(
    |         -------- required by a bound introduced by this call
101 |             tuple((tag(key.clone()), multispace0::<T, Error<T>>, char(':'), multispace0)),
102 |             parser,
    |             ^^^^^^ expected an `FnMut<(T,)>` closure, found `P`
    |
    = note: required for `P` to implement `Parser<T, _, nom::error::Error<T>>`
note: required by a bound in `preceded`
   --> /home/merlin/.cargo/registry/src/github.com-1ecc6299db9ec823/nom-7.1.3/src/sequence/mod.rs:69:6
    |
69  |   G: Parser<I, O2, E>,
    |      ^^^^^^^^^^^^^^^^ required by this bound in `preceded`
help: consider further restricting this bound
    |
97  |         P: nom::Parser<T, T, E> + std::ops::FnMut<(T,)>,
    |                                 +++++++++++++++++++++++

Solution

  • Because you have used &'a str in your parser, I guess you don't need it be generic over other input types.

    fn extract_unsigned_value<'a, K, F, P, O, O1>(key: K, parser: P, c: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, Error<&'a str>>
        where F: FnMut(O1) -> O,
              P: nom::Parser<&'a str, O1, Error<&'a str>>,
              K: InputLength + Clone,
              &'a str : Compare<K>
    {
            map(preceded(
                tuple((tag(key), multispace0, char(':'), multispace0)),
                parser,
            ), c,
            )
    }
    
    fn main() {
        let mut parser = extract_unsigned_value("Test", digit1, |string| string.parse::<u32>().unwrap());
        let r = parser("Test : 345");
        println!("{r:?}")
    }
    

    Also I have removed the closure, because map already returns one.

    Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c1c2db4e49973d8f15291cee0b28464d