futures-rs
futures-rs copied to clipboard
FusedFuture is incoherent
The documentation for FusedFuture is quite vague. Unfortunately, I think this vagueness has enabled incoherence to arise and the situation is now rather poor:
Some reasoning
I think let r = select!{ r = fut => r }; should work the same way as let r = fut.await;. Otherwise select!'s behaviour is inconsistent with the behaviour of the supplied futures.
select! doesn't poll any futures whose FusedFuture::is_terminated returns true.
This means that FusedFuture::is_terminated must not return true if the future would return Ready; nor if it would squirrel away a Waker from the Context.
select!'s complete => feature relies on is_terminated. The documentation is not entirely clear, but certainly is_terminated there must return true if the future has already returned Ready.
The most reasonable interpretation of all this is_terminated should return precisely whether the future has already returned Ready.
However, many futures in futures-rs implement FusedFuture despite being unable to answer this question definitively. For example, OptionFuture, oneshot::Receiver and probably others. Others answer it wrongly: for example, future::Pending has is_terminated always return true and this is even used in a test case (async_await_macros.rs, select_with_complete_can_be_used_as_expression).
Options
-
Declare
FusedFuture::is_terminatedis supposed to say precisely whether the future has returnedReady. Remove the impls for futures that don't know whether they've already returnedReady(which is many of them) and have users who need this functionality call.fuse(). Fixfuture::Pending. etc. This is an API break. -
Leave
FusedFuture::is_terminatedas it is, but plaster it, andcomplete =>, with big warnings saying it's deprecated. Haveselect!withoutcompletenot ever callis_terminated. This fixes the behaviour ofselect!to be cosistent withawait, and leaves users ofcompletealone. This is arguably a bugfix, although it might need a crater run or something. It seems like a practical approach to making this better, pending the probably still-long-distant 1.0 release. -
As 2, then: Introduce a new version of
completewith a new name and a new trait to support it. For use in tests and corner cases, introduce a new "always looks like it has been awaited" future which is alwaysPendingand (unlikefuture::Pending) is always "complete" for the purposes ofselect!. -
Tell everyone to use Tokio which doesn't have these bugs.
References
https://github.com/rust-lang/futures-rs/issues/1989 request for select! to work without FusedFuture, based on conditional branches (which Tokio's select! has). Fairly extensive discussion of some other problems with FusedFuture.
https://github.com/rust-lang/futures-rs/issues/2475 report of brokenness with oneshot::Receiver
https://github.com/rust-lang/futures-rs/issues/2455 report of confusion with OptionFuture and Pending
https://github.com/rust-lang/futures-rs/issues/2213 trouble with future::Map
FusedStream
This trait is very similar to FusedFuture. My examination of the source in futures-rs suggests that at least some of its impls have similar bugs to the bugs in the impls of FusedFuture. But I haven't investigated FusedStream properly and don't really know why it exists (and why it has a different API approach to FusedIterator)