4

I am writing a nom parser combinator that consumes bytes and returns an Option<&[u8]>. The parser combinator should follow the following rules:

  1. Read a single signed, big endian 16 bit integer (s)
  2. If s is -1, return None
  3. If s is not -1, read s bits and return Some

Here is my parser combinator:

fn parse(i: &[u8]) -> nom::IResult<&[u8], Option<&[u8]>> {
    nom::combinator::flat_map(be_i16, |s: i16| {
        match s {
            -1 => nom::combinator::success(None),
            _ => nom::combinator::map(take(s as u16), Some)
        }
    })(i)
}

However I am seeing the following error:

error[E0308]: `match` arms have incompatible types
  --> src/main.rs:15:18
   |
13 | /         match s {
14 | |             -1 => nom::combinator::success(None),
   | |                   ------------------------------ this is found to be of type `impl Fn<(_,)>`
15 | |             _ => nom::combinator::map(take(s as u16), Some)
   | |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
16 | |         }
   | |_________- `match` arms have incompatible types
   |
  ::: /Users/dfarr/.cargo/registry/src/github.com-1ecc6299db9ec823/nom-6.0.1/src/combinator/mod.rs:74:64
   |
74 |   pub fn map<I, O1, O2, E, F, G>(mut first: F, mut second: G) -> impl FnMut(I) -> IResult<I, O2, E>
   |                                                                  ---------------------------------- the found opaque type
   |
   = note:     expected type `impl Fn<(_,)>`
           found opaque type `impl FnMut<(_,)>`
   = note: distinct uses of `impl Trait` result in different opaque types

I can see that nom::combinator::success does indeed have a return type of impl Fn(I) -> ..., and nom::combinator::map returns impl FnMut(I) -> .... Is there a more idiomatic way of using nom where these combinators can be used together in this manner?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
David Farr
  • 123
  • 1
  • 2
  • 5
  • Just to be clear, you want to parse zero-to-many `i16`s until you encounter `-1` or? – vallentin Jan 13 '21 at 21:23
  • Just a single `i16` value, I've updated the description. This value is guaranteed to exist, for some context I am parsing the kafka protocol, please see the [variable length primitive section](https://kafka.apache.org/0100/protocol.html#protocol_details) of the kafka docs. – David Farr Jan 13 '21 at 21:34

1 Answers1

1

This may do the trick (it compiles). Here, nom::combinator::map applies the transform to the successful result value and re-wraps it in the Ok variant. Incidentally, there is also a nom::Parser<T>::map which seems to implement the Map iterator.

use nom::combinator::map;
use nom::number::complete::be_i16;

fn parse(i: &[u8]) -> nom::IResult<&[u8], Option<i16>> {
    let f = |s: i16| if s == -1 { None } else { Some(s) };
    map(be_i16, f)(i)
}
George
  • 2,451
  • 27
  • 37