Why isn't there a `future.blocking_await()`?

Just had to write this myself, and it surprised me that it's not already built-in as part of Futures.

pub trait BlockingAwaitable: Future {
    fn blocking_await(self) -> Self::Output;
}
impl<T> BlockingAwaitable for T
where
    T: Future,
{
    fn blocking_await(self) -> Self::Output {
        futures::executor::block_on(self)
    }
}

Sure it depends on the async executor, but surely it would make sense as part of the traits the executor needs to implement?

It would let calling an async function and waiting for the results be closer in sync and async functions:

async {
 some_future.await
};
// and
some_future.blocking_await();
// instead of
block_on(some_future);

Not all executors can implement this. For example, a single-threaded executor will get here, block on the completion of…some other future that it can no longer execute because it is blocked here (e.g., blocking on consuming from a channel while some other future would be allowed to produce a value but can no longer be scheduled).

Feels like a highway-to-deadlocksville footgun to me if it were so accessible.

3 Likes

I don't think that an extension trait is the right call here, but we could definitely have a std::future::block_on function, like the popular pollster crate provides.

1 Like

Wait, then how do you access the result of a single-thread-executed future from non-async code? Is that just not a thing?

Usually you start a new executor, move the future to it, then block. If you ask the current executor, it may have to do other things to satisfy the final await and need to get a Pending from it so it is "allowed" to work on other futures in the meantime.