Simplifying closure return types

Hello,

I'm writing parser utilities on top of nom, so I'm going to have lots of functions that return closures. To decrease verbosity and enforce consistency, I tried to create some type aliases like so:

use nom::IResult;
use nom_locate::LocatedSpan;
use crate::parse::error::PError;

pub type Span<'a> = LocatedSpan<&'a str>;
pub type PResult<'a, T> = IResult<Span<'a>, T, PError>;
pub type Parser<'a, T> = impl Fn(Span<'a>) -> PResult<'a, T>;

However, the last line is illegal:

error[E0658]: `impl Trait` in type aliases is unstable
 --> src/parse.rs:9:26
  |
9 | pub type Parser<'a, T> = impl Fn(Span<'a>) -> PResult<'a, T>;
  |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information

error: unconstrained opaque type
 --> src/parse.rs:9:26
  |
9 | pub type Parser<'a, T> = impl Fn(Span<'a>) -> PResult<'a, T>;
  |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `Parser` must be used in combination with a concrete type within the same module

For more information about this error, try `rustc --explain E0658`.

And no, I don't want to use unstable or pack everything into one module. Is there any other way to simplify function definitions that return closures? Thanks!

Best, Oliver

The issue is that impl Trait is not a type. It's a placeholder which can only be resolved to a concrete type in its own context, e.g. as the return type of a function, it depends on the implementation of the function.

To solve the problem, you could create a blanket impl:

pub trait Parser<'a, T>: Fn(Span<'a>) -> PResult<'a, T> {}

impl<'a, T, P> Parser<'a, T> for P
where
    P: Fn(Span<'a>) -> PResult<'a, T>
{}

Then you'll be able to use Parser<'a, T> and Fn(Span<'a>) -> PResult<'a, T> synonymously, so you'll be able to write -> impl Parser<'a, T>. Playground

Yes, thank you, that's what I needed. That works even better than a type alias in my case, because in some places, I want a trait, too.

Now I remember, I had seen this trick before, but then forgot. Effectively, a trait alias. I wish Rust had a simpler way to alias traits. Maybe like this?

trait Parser<'a, T> = Fn(Span<'a>) -> PResult<'a, T>;

There's the unstable trait_alias feature for that.

You could also consider writing a simple macro.

Great, looking forward for that to become stable!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.