Description
In programming languages where futures are callback-based and can make progress by themselves without anyone actively awaiting them, the following pattern is common for starting several actions concurrently, and waiting then for them all to complete:
future1 = do_something_async1()
future2 = do_something_async2()
res1 = await future1
res2 = await future2
However if you write code like that in Rust, the actions will be executed sequentially, since Rust futures require you to actively poll them to drive them to completion, and you won't be polling future2
while you're .await
ing future1
.
Currently, the compiler does not warn on the following code:
let future1 = async { };
let future2 = async { };
future1.await;
future2.await;
It would be nice if the compiler could catch situations like these and suggest to use futures::join()
instead.
As a first approximation, the heuristic could be: warn if .await
is used when a variable whose type is a Future
is in scope (and is not the value being awaited) — meaning that other future won't make progress while we're awaiting this one. So in the example above, future1.await
is performed while future2
is in scope.
This heuristic would not catch (false negative) the following pattern:
let futures: Vec<SomeFuture> = iter.map(make_future).collect();
for future in futures {
future.await;
}
which, in other languages, is also a common pattern to preform and await a dynamic number of actions concurrently. In Rust, you have to use something like futures::join_all()
or FuturesUnordered
.
False positives (where you don't actually have to poll your future for it to make progress):
- Join handles of spawned tasks
- Channel receivers
- Timers
Perhaps there could be an attribute to mark these types.
Related: rust-lang/wg-async#16; but that RFC is about specially marking some types that should not be held across .await
, whereas this issue is about applying that to (almost) all futures.