Open zjijz opened 1 year ago
#[derive(Debug)]
warning.@taiki-e Any thoughts on this?
This doesn't seem to impose any oddities for me when using the await
path to consume a Future
(possible wrapped in combinators) since all cloning would have to happen before the Future
was ever polled once.
However, with the ability to easily poll with things like FutureExt::poll_unpin
or Box::pin
+ Future::poll
, I'm worried that some of the Future
implementations weren't written with the possibility of cloning in mind and might clone a Future
's intermediate state instead of fully recreating the initial version (which might be what people expect, but seems to be an edge case when I think about it). Lazy
is one example of this that if cloned after a call to poll_unpin
would return a Lazy
that always panics on the next poll.
Thanks for the PR. Which future combinator is actually needed for your use case?
Map
is the big one for my use case. I'm not sure the current implementation lends itself to Clone
being thrown on it to where it can potentially panic if there was a clone
thrown in between or after manual polls. I can also write my own Map
that is less panicky about polling after completion if you don't want to expose the potential footgun here.
This updates most
Future
combinators to preserveClone
when available on the baseFuture
.For motivation, imagine you have some complicated
Future
that is notClone
and requires.shared()
to properly share it. Then imagine you have a library function that is meant to bundle together a bunch of combinators to fulfill some semantic purpose. That library function will have to call.shared()
if it wants to try to guarantee the returnFuture
isClone
, but this might be suboptimal if the inputFuture
was alreadyClone
, plus it obfuscates the.shared()
allocation. With this change, you can instead requireFuture + Clone
on the inputFuture
and have a guarantee the output will beClone
as well.The hold-out
Future
implementations are:Remote
/RemoteHandle
due to their use offutures_channel::oneshot::{Sender, Receiver}
. This seems like it is by design that these cannot beClone
.JoinAll
/TryJoinAll
due to their use ofStream
combinators, but also that it seems unlikely that people would expect them to offerClone
since they are used to perform a potentially costly sync barrier that would probably be desired to happen only once.For the hold-outs, the existing pattern of using
.shared()
allows forClone
, and follows the intended semantics of those combinators.Some combinators that might not make the most sense to preserve
Clone
:IntoStream
TryFlattenStream
If these changes make sense, I think it would also make sense to apply them to
Stream
combinators as well (although I don't see myself utilizing this property as much with them). If that is the case, theseFuture
->Stream
combinators make sense to preserveClone
.Tested:
cargo doc
.cargo fmt
.cargo test --all-features
.