Trait Parametric Polymorphism - latest updates?

Does anybody know the latest updates for Associated Traits and other kinds of Trait Parametric Polymorphism? The RFC repo issue describing what it could broadly be is here. Centril also made a GitHub repository for collecting notes, but it appears that he has been inactive lately. Does anybody know the latest on this?

1 Like

I looked at the RFC, and I think that we won't need this once type_alias_impl_trait is stabilized. There's this example:

trait Handler {
    trait Arg;
    fn handle<ArgImpl: Self::Arg>(&self, arg: ArgImpl);
}

struct MyHandler;

impl Handler for MyHandler {
    trait Arg = IntoIterator<Item = i32>;
    fn handle<ArgImpl: Self::Arg>(&self, arg: ArgImpl) {
        for number in arg {
            println!("{}", number);
        }
    }
}

which can be written as

trait Handler {
    type Arg;
    fn handle(&self, arg: Self::Arg);
}

struct MyHandler;

impl Handler for MyHandler {
    type Arg = impl IntoIterator<Item = i32>;
    fn handle(&self, arg: Self::Arg) {
        for number in arg {
            println!("{}", number);
        }
    }
}

This doesn't compile, but I'm not sure why.

EDIT: It doesn't compile because type_alias_impl_trait only works for existential types (like impl Trait in return position), so my example is wrong.

I think the compiler's error is spot-on here unfortunately...

error: could not find defining uses
  --> src/lib.rs:11:16
   |
11 |     type Arg = impl IntoIterator<Item = i32>;
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

There is, indeed, no defining use. impl Trait (in this position) as I understand it means "there is exactly one type satisfying this constraint", whereas trait Arg refers to the constraint itself, and is therefore more powerful, and can be used as a bound itself. There is no defining impl in the example - which type that implements IntoIterator should the compiler pick for Arg?

In any case, while some things may be fixed by this, it doesn't solve the other issue of writing something like

type BoxFuture<'a, trait Bounds, T>(pub Pin<Box<dyn Future<Output = T> + Bounds + 'a>>);

which is what I personally want to use. This is very nice for being generic over the Sendness of a Future, and propagating it up without imposing restrictive bounds.

1 Like

I don't think so. impl Trait in argument position is equivalent to a generic parameter, e.g.

fn handle(arg: impl IntoIterator<Item = i32>) {}

Yes, but not in the position you put it in, as far as I am aware. (Side note: this is why I am iffy about using impl Trait in argument position in the first place.)

As an example, this compiles:

trait Handler {
    type Fut: Future<Output = ()>;
    fn handle() -> Self::Fut;
}

impl Handler for () {
    type Fut = impl Future<Output = ()>;
    fn handle() -> Self::Fut {
        async {}
    }
}

In this position impl Trait is existential, not universal (I believe that is the terminology? not sure). I.e, there is one type for the impl, which is the GenFuture generated by the async {} block. While it could work syntactically to reuse impl Trait as a universal for associated traits, this is not implemented, and would also conflict with the existential use as shown above.

Thanks, that explained it!

1 Like