Impl custom Future as a select operation over a vec of futures

Given

struct Futures<F: Future<Output = u8>> {
    futures: Vec<F>
}

I want to make Futures a future. So I can await it.

impl<F: Future<Output = u8>> Future for Futures<F> {
    type Output = u8;
    fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
         todo!()
    }
}

When I await Futures struct, I want it to poll each of its member self.futures in a select. That is, it should poll each future in Vec<F> until one is ready. In which case, Futures should report that it is ready and return the result produced by the ready future.

I'm not trying to solve a basic problem already covered in a crate or std. I don't need select!{}. This is just a simplified minimum example.

The first thing you’ll need to sort out is how to pin the contained futures: Once a future is polled for the first time its address can’t be allowed to change, but Vec can reallocate and move its contents whenever it is modified.

There are a few ways to do that:

  • Store Pin<Box<F>> inside the vector instead
  • Instead of a vector, store an array [F; N] (adding N as a const generic parameter to the struct)
  • Manually ensure that you don’t do anything that might make the vector reallocate, and use unsafe to pin the references yielded from Vec::iter_mut
  • Find a crate that provides a Vec-like collection which pins its contents (probably something-arena)

Once you've figured that out, implementing Future is straightforward:

impl<F: Future> Futures<F> {
    fn iter_pinned(self: Pin<&mut Self>)->impl Iterator<Item=Pin<&mut F>> {
        todo!()
    }
}

impl<F: Future> Future for Futures<F> {
    type Output = F::Output;
    fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
        for f in self.iter_pinned() {
            if let poll @ std::task::Poll::Ready(_) = f.poll(cx) { return poll; }
        }
        std::task::Poll::Pending
    }
}
5 Likes