Could returning `impl Trait` in a trait be allowed here?

impl Trait in return position is not supported in traits (not even with nightly features). However, I think it should be easy to implement, if the return type is not constrained by generics and lifetimes defined in the function:

trait Foo<'a> {
    fn bar(&'a self) -> impl Debug + 'a;
}

// this is roughly equivalent to

trait Foo<'a> {
    type Bar: Debug + 'a;

    fn bar(&'a self) -> Self::Bar;
}

The above code with the associated type works, and with #![feature(type_alias_impl_trait)], it's even possible to use it for opaque types:

impl<'a> Foo<'a> for String {
    type Bar = impl Debug + 'a;

    fn bar(&'a self) -> Self::Bar {
        // note that this type can't be named!
        self.chars().filter(|c| c.is_ascii())
    }
}

However, it would be even nicer if impl Trait in return position was allowed here. What do you think?

AFAIK this has pretty much been the plan for years, just blocked on more fundamental trait system work.

http://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/#complication-1-returning-impl-trait-in-traits-is-not-supported

impl Trait in traits requires GATs

Conceptually, GATs are fairly simple. Implementation wise, though, we’re still working on how to support them in rustc – this may require porting rustc to use chalk, though that’s not entirely clear. In any case, this work is definitely underway, but it’s going to take more time.

RF 2071 included the type Foo = impl Trait; feature under a different syntax, though iirc not long ago everyone agreed that type Foo = impl Trait; is the best syntax for it after all. https://github.com/rust-lang/rust/issues/63066 includes it explicitly on the checklist of impl Trait-related things we need to do.

EDIT: That same checklist also has https://github.com/rust-lang/rust/issues/63066 for the "just a type alias" case of this syntax, which appears to be what you're specifically asking about. I don't know what the status of that is, but I wouldn't be surprised if it's also simply waiting on all of the other fundamental work to reach a tipping point.

1 Like

If we do this we could also add non-self-referential async functions in traits.

Note that the OP is specifically asking about the case that does not require GATs

2 Likes

Unfortunately there’s no syntax for this other than writing out the -> impl Future return type, async fn always capture all arguments even if they’re unused.

One downside to this shorthand is you lose out on the ability to add extra constraints to the associated type like

where
  T: Foo<'a>
  T::Bar: Send

Or eventually (hopefully)

where T: Foo<'a, Bar: Send>

Why do you say that you lose the ability to add extra constraints? Shouldn't this work (eventually)?

type T: impl Foo<'a, Bar: impl Send>

Note: I think that this requires GAT, so it's outside of what OP is asking.

I think what @Nemo157 meant is this:

trait A {
    type Output: ToString;
    fn a() -> Self::Output;
}

trait B {
    fn b() -> impl ToString;
}

fn foo<T: A, U: B>()
where
    T::Output: Send;
    // you can't constrain the return type of U::b() here!

That's right, but I'm pretty sure you can still do something like this (not tested):

fn foo<T: A, U: B>()
where
    <T::b as Fn() -> _>::Output: Send;

And with GAT, it would be easier to extract the return type of function. C++ is using that kind of tricks since ages, so I don't see why it wouldn't be possible to implement them in Rust (those tricks may requires GATs however).

Potentially, though probably written closer to

fn foo<T: Foo, U>(_: T)
where
    T::b: Fn() -> U,
    U: Send

(Since Fn::Output is unstable, and you can't have placeholders inside type bounds). Doesn't work currently because b doesn't exist in the associated type namespace, and as far as I know there is no way to reference the type of the associated function. So would need at least an additional new feature to support that somehow.

(And note that it can't just be to introduce associated functions directly into the type namespace too, trait Foo { type bar; fn bar(); } is valid).

2 Likes

I don't think T::b can be referenced as a type at all. Rust complains: "associated type b not found for T".

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.