Will we be able to return impl Trait from default trait methods?

This seems not possible currently:

trait MyTrait {
    fn foo() -> impl Trait;
}

However this is possible:

trait MyTrait {
    fn foo<T: Trait>() -> T;
}

But is only really useful when you have some way of returning the exact same generic T, for example with a function in Trait returning Self.

From a syntax perspective, it would be nice if these two versions both were valid and equivalent, because -> impl Trait is much more readable.

But this is still very rarely useful and does not really express "foo returns something implementing Trait", but rather "given some type implementing Trait, foo can return it", i.e. the type needs to be known to the caller.

This introduces a lot of coupling in the code because whenever foo changes, users of foo need to adapt. It would be better if callers would just have to know "whatever foo returns, it implements Trait".

As an example, foo might want to return some type of Iterator, but not need to be specifically commited to any concrete implementation of an Iterator, so that it can easily change in the future without disrupting callers.

The way to achieve this right now seems to be:

trait Trait {}

struct A;
impl Trait for A {}

trait MyTrait {
    type T: Trait;
    fn foo() -> Self::T;
}

struct Imp1;
impl MyTrait for Imp1 {
    type T = A;
    fn foo() -> Self::T {
        A
    }
}
struct Imp2;
impl MyTrait for Imp2 {
    type T = A;
    fn foo() -> Self::T {
        A
    }
}
fn main() {
    let a: A = Imp1::foo();
    let a2: A = Imp2::foo();
}

So we have to explicitly define each implementation to give it the exact type we want to return, but this means we can't use default trait methods, because we can't set a default for an associated type.

My question is, will it eventually be possible to define default trait methods returning impl Trait, or a default associated type? Which RFCs are relevant to this? I have seen associated_type_defaults, which would help a bit, but using the nightly version it was not possible to "assume associated type defaults in the trait defining them" (playground).

And it would also be useful if the concrete return type could be implicit, i.e. we can define a default trait method returning some trait, the concrete type is inferred from the returned type in the default method, but never mentioned explicitly. When an implementation of the trait overrides the default, it could be forced to use the same concrete type as the default implementation if needed. Although callers should only expect a type implementing the trait, so shouldn't it be possible to return any type implementing the mentioned trait?

I think the feature you want is type_alias_impl_trait, RFC 2515.

3 Likes

Yes, this would solve the explicit case, but ultimately it would be even better if a hidden associated type could be inferred from the returned type in the function. So only really

struct A;
impl Trait for A {}

trait MyTrait {
    fn foo() -> impl Trait {
        A
    }
}

would be needed.

I tried using associated_type_defaults together with type_alias_impl_trait but they don't seem to be compatible yet.

I remember that the specialization RFC mentioned as "future work" "specialization groups" - i.e. allowing you to write:

impl<T> Trait for T {
    default {
        type Ret = i32;
        fn foo() -> Self::Ret { 0 }
    }
}

Then foo() and Ret has to be specialized together, and so you can rely in your defaulted implementation of foo() that Ret is i32.

2 Likes