Sorry that you feel I glossed over this, I definitely do feel the pain of this point, I really don't like that I had to write a function signature like
pub fn read_exact<'a, 'b: 'a, R: Read + 'a>(
mut this: Pin<'a, R>,
buf: &'b mut [u8],
) -> impl StableFuture<Item = (), Error = Error<R::Error>>
+ Captures<'a>
+ Captures<'b>
But, I still feel that this is better solved as part of impl Trait
than just avoided in async fn
. Even if futures
is the only use case where the current impl Trait
lifetime capture rules are not the best; there are always going to be some functions that are easier to write using combinators instead of async fn
, these are likely to have the exact same problem of capturing arguments in their closures and having to use an impl Future
return type with way too many lifetime bounds.
In my current prototype (and, I believe, forced by the definition of Future
and Stream
) there's only one case (well, family of cases because of the error type) where the return value can implement both
for<E> impl Future<Item=(), Error=E> + Stream<Item=!, Error=E>
I would argue these two traits are isomorphic so there's no real problem being able to return a type that implements both.
This is a super-interesting idea, if you expand on it in your planned RFC I definitely can't wait to see it.
One case you didn't mention is "nominal existential types", I have a set of traits I hope to eventually model using ATC and implement via nominal existential types, e.g. the nominal existential type + impl Trait
in trait examples are actually split from one trait I want to be able to write:
pub trait CountDown {
type Counting<'a>: Future<Item = (), Error = !> + 'a;
fn start(&mut self, count: Duration) -> Self::Counting<'_>;
}
impl CountDown for Timer {
abstract type Counting<'a>: Future<Item = (), Error = !> + 'a;
#[async]
fn start(&mut self, count: Duration) -> Self::Counting<'_> {
...
}
}
I'm still not certain that I need this over just using raw impl Trait
in trait support, but this allows for adding additional constraints on the returned future:
fn foo<C>(countdown: C)
where
C: CountDown,
<C as CountDown>::Counting: Send + Unpin,
I guess the oft-mentioned typeof
operator might provide an alternative that works with just impl Trait
in trait
fn foo<C>(countdown: C)
where
C: CountDown,
typeof(<C as CountDown>::start)::Output: Send + Unpin,