-
Notifications
You must be signed in to change notification settings - Fork 655
Description
A common situation in my current project is that I need to select!
between the results of calling async methods on various things that might not exist. Unfortunately the select macro doesn't seem to have any native support for conditionally including branches in the select, something that would be like
futures::select! {
if let Some(ref mut obj) = maybe_existing_object {
event = obj.async_method().fuse() => do_something_with(event).await
},
event = always_existing_object.async_method().fuse() => do_something_with(event).await,
}
And it's not possible to do maybe_obj.map(|o| o.async_method().fuse()).unwrap_or_else(|| future::Fuse::terminated())
(or future::pending()
etc.) because, well, async methods — that never-resolving future we use for when the object doesn't exist doesn't match the impl Future
opaque type of the async function, so they're incompatible unless we go with the boxing and dyn
route and all that inefficiency and sadness.
So far this is the best thing I could come up with:
pub struct MaybeFuture<F>(Option<F>);
impl<F> MaybeFuture<F> {
pub fn new(f: Option<F>) -> MaybeFuture<F> {
MaybeFuture(f)
}
}
impl<F: Future + Unpin> Future for MaybeFuture<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
// XXX: unchecked should be fine here, is it faster than Unpin?
if let Some(ref mut f) = self.get_mut().0 {
Future::poll(Pin::new(f), cx)
} else {
task::Poll::Pending
}
}
}
impl<F: FusedFuture + Unpin> FusedFuture for MaybeFuture<F> {
fn is_terminated(&self) -> bool {
if let Some(ref f) = self.0 {
f.is_terminated()
} else {
true
}
}
}
futures::select! {
ev = this.keyboard_events.select_next_some() => this.on_keyboard_event(ev).await,
ev = MaybeFuture::new(this.ptr.as_mut().map(|p| p.next())) => this.on_pointer_event(ev).await,
ev = MaybeFuture::new(this.touch.as_mut().map(|p| p.next())) => this.on_touch_event(ev).await,
// ......
}
Maybe there should be direct impl<F: Future> Future for Option<F>
like this? Or native syntax for conditionally including select!
branches depending on Option
s being Some
or None
(but that might be more difficult)?