"impl Trait" should be supported in the return type of a closure

In the current, impl Trait is only allowed in function and inherent method return types, which means, we cannot convenient to implement such a function

let f = || -> impl Fn()->(){
   ||{};
};
f()();

A closure acts like a method, so it is reasonable that impl Trait allows to appear in the return types of closures.

Is there a specific reason you can't just rely on type inference?

let f = || {
   ||{}
};
f()();

The singular return type needs to be inferable for the -> impl Trait to compile anyway, so I don't think there's ever a place where leaving the closure return type inferred wouldn't compile but -> impl Trait could.

You do need to name the type somehow if you're returning it from a function. While the compiler won't allow that with Fn() -> _ type syntax, it is allowed with associated types like the raw Fn::Output:

#![feature(unboxed_closures)]

fn f() -> impl Fn<(), Output = impl Fn() -> ()> {
    || {
        || {}
    }
}

fn main() {
    f()()();
}
3 Likes

I don't think there is much disagreement that impl Trait in closures should be supported, but it's a bit complex to implement. This basically depends on GATs and has the same issues as async functions in traits.

3 Likes

Consider that I want to design a trait for creating curry functions for the type that implements the trait

trait Curry<T, U>{
   fn to_curry(self)->impl Fn(T)->impl Fn(U)->i32;
}
impl<T,U> Curry for fn(T,U)->i32{
   fn to_curry(self) ->impl Fn(T)->impl Fn(U)->i32{
      move |v0:T|{
       move  |v1:U|->i32{
           self(v0,v1);
       }
     }
   }
}
fn show(v:u8,v2:i32)->i32{
   0
}
fn main(){
  let f = (show as fn(u8,i32)->i32).to_curry();
  f(0)(1);
}

In the current, the above trait is not supported. The impl Trait syntax is neither allowed in the return type of closure nor the trait method. This decreases some expressive abilities. So, the goal in the above code is hard to implement in the current rust. In the current rust, we can only implement the curry by using Box<dyn Trait> trait object, which is not convenient and the code is hard to read and maintain.

Allow impl Trait in the return type of closure and trait method is useful and reasonable.

This will soon be possible via type-alias-impl-trait (playground)

impl<F, T, U, R> Curry<T, U, R> for F
where
    F: FnOnce(T, U) -> R + Copy,
{
    type First = impl FnOnce(T) -> Self::Second;
    type Second = impl FnOnce(U) -> R;

    fn to_curry(self) -> Self::First {
        move |v0: T| self.make_second(v0)
    }

    fn make_second(self, v0: T) -> Self::Second {
        move |v1: U| -> R { self(v0, v1) }
    }
}

(it's annoying that you have to have that second method on the trait, I couldn't find any way to do that as an entirely internal helper function because of not being allowed to inherit the generic Self type and limitations around lazy normalization of the defining uses).

3 Likes

I discovered there is a nightly feature already to allow this, although it's not suggested by the error message, so this does work:

#![feature(impl_trait_in_fn_trait_return)]

fn f() -> impl Fn() -> impl Fn() -> () {
    || {
        || {}
    }
}

fn main() {
    f()()();
}

edit: I opened #105408 to add the feature suggestion as help in the error output.

2 Likes

Support impl Trait in the type alias is wonderful but it's a bit trivial to use Self::First in place of directly writing impl Triat in the trait method return type.

I think this is insufficient. We are looking forward that impl Trait can also be allowed in the trait method return type.

Yeah, I understand, these are different positions to be considered.

1 Like