Help with nom error

I'm having a hard time understanding this error.

In this example, foo1 compiles, but foo2 does not.

use nom::Parser;

fn foo1<'a>() -> impl Parser<&'a str, i8, ()> {
    nom::combinator::flat_map(nom::character::complete::u32, |_| {
        nom::character::complete::i8
    })
}

fn foo2<'a>() -> impl Parser<&'a str, i8, ()> {
    nom::character::complete::u32.flat_map(|_| nom::character::complete::i8)
}

The error message I get is:

error[E0277]: the trait bound `nom::FlatMap<fn(_) -> Result<(_, u32), nom::Err<_>> {nom::character::complete::u32::<_, _>}, [closure@log-parser/src/foo.rs:22:44: 22:47], u32>: nom::Parser<&'a str, i8, ()>` is not satisfied
  --> log-parser/src/foo.rs:21:18
   |
21 | fn foo2<'a>() -> impl Parser<&'a str, i8, ()> {
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `nom::Parser<&'a str, i8, ()>` is not implemented for `nom::FlatMap<fn(_) -> Result<(_, u32), nom::Err<_>> {nom::character::complete::u32::<_, _>}, [closure@log-parser/src/foo.rs:22:44: 22:47], u32>`
   |
   = help: the trait `nom::Parser<I, O2, E>` is implemented for `nom::FlatMap<F, G, O1>`

I'm aware that the standalone function nom::combinator::flat_map returns (in my case) an impl FnMut(&str) -> IResult<&str, i8, ()>, while the trait method nom::Parser::flat_map returns a FlatMap<_, _, u32>. However, I don't know why the error message says that the trait bound is not satisfied. The "help" message even says:

the trait `nom::Parser<I, O2, E>` is implemented for `nom::FlatMap<F, G, O1>`

So what about the trait bound is failing? It would be great to get some advice both in this specific case and the more general "how do I determine why a trait bound is failing?" question.

Thank you!

This flat_map method takes a FnMut implementaton but the Parser implementation requires a Fn implementation. Putting the closure directly in the flat_map invocation poked the compiler in the direction of deciding your closure should be a FnMut closure only, and not the more general Fn closure.

Poking the compiler to decide certain things about a closure by putting it directly in a function invocation is helpful in overriding inference in some cases, but in this case, it backfired. You can add your own helper to counteract it in this case (shown in the playground below).


When I'm faced with complicated Rust code and I'm not sure what the type of a given value is, I can usually just do this:

let _: () = the_value;

And the compiler will tell me. When it comes to failing trait bounds, the analogous option is to set up your own function with the trait bounds and feed values through it, so you can get more details about what's breaking.

fn helper2<I, O1, O2, E, F, G, H>(f: FlatMap<F, G, O1>) -> FlatMap<F, G, O1>
where
    // vvv the bounds from the `impl Parser for FlatMap`
    F: Parser<I, O1, E>,
    G: Fn(O1) -> H,
    H: Parser<I, O2, E>,
{ f }

Usually that's enough to get the information you need, but when it isn't, you can start eliminating bounds to hone in on the problematic one.


3 Likes

@quinedot thank you very much! Fn vs. FnMut makes sense, I was looking at all of the surrounding things, not those traits.

I've done the let _: () = value trick before. I've never seen anyone describe the function trait bound approach, although I was starting to wander towards something like that in this exploration. I should have gone a little further.

The fact that the compiler can decide if a closure is Fn, FnOnce, or FnMut is interesting. What's even more weird (for me at least) is that you can convince it to do something different with seemingly innocuous changes.

Replacing this

fn foo<'a>() -> impl Parser<&'a str, i8, ()> {
    nom::character::complete::u32.flat_map(
        helper(|_| nom::character::complete::i8)
    );
}

with this

fn foo<'a>() -> impl Parser<&'a str, i8, ()> {
    nom::character::complete::u32.flat_map({
        let f = |_| nom::character::complete::i8;
        f
    })
}

makes it compile. Weird! Playground link.

I don't know if anyone has any ideas on why that simple change fixes things, I would be open to hearing about it. Or is anyone aware of any RFCs or tracking issues to make this behave a little better? :thinking:

Just based on experience, the closure definition itself needs to be in some place with the desired bound. You've moved the definition to some place with no ascription or bound, and the compiler chooses what to implement based on the body as per usual. I don't know of any official documentation on what the actual rules/behaviours are (i.e. when the usual inference gets overridden).

This particular case is arguably a bug (I wouldn't be surprised if one exists, but failed to find one), as a Fn-implementing closure also implements FnMut. On the other hand, I think I've seen the need to override what "flavor" of closure is inferred before (but can't recall the details offhand).[1]

RFC 3216 (merged but not stable) addresses one common closure inference pitfall, but I'm unaware of an RFC or other plans to give explicit notation to otherwise inferred properties of closures more generally.


  1. Might have been forcing it to be more general, not less, though? ↩ī¸Ž