-
Notifications
You must be signed in to change notification settings - Fork 625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rethink relationship between Future
and StableFuture
#949
Comments
So, my experiment did not go well, having |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For reference, the current APIs
These APIs made sense with the original
Pin
andasync
/await
APIs but there have been two changes since that I believe mean we can rethink these:await
now goes viaFuture::poll
for#[async_move]
Pin
andPinBox
are bothUnpin
Together these suggest an alternative design for the relationship between
StableFuture
andFuture
that is more "correct", has nounsafe
code, and should support a superset of the operations currently supported:Previously
await!
inside#[async_move]
usedStableFuture::poll
, this was the original reason for adding theimpl<F: Future> StableFuture
. Now that it usesFuture::poll
and users must pin anyStableFuture
via something likeawait!(foo.pin())?
this isn't necessary for this use case.Instead all that is necessary is to acquire something that implements
Future
from aStableFuture
. Currently that can be done only viaStableFuture::{pin,pin_local}
which internally uses an unsafe layer to implementFuture
on top of aStableFuture
.We can change
StableFuture::{pin,pin_local}
to instead return the more "correct" (and safe)PinBox<StableFuture ...>
(or drop these functions and have users just callPinBox::new
themselves) but still have this: Future
via adding the three safe implementations shown above.The first uses the property that
for<T: Unpin> Pin<T> == &mut T
, therefore a safe implementation ofFuture
for any type that implementsStableFuture
can be added. The second and third are just the standard forwarding implementations of object safe traits (usingPin
andPinBox
instead of the normal&mut
andBox
). Combined these result inPinBox<StableFuture>: Future
and can be used directly inawait!
inside#[async_move]
.I believe these changes can support more ergonomic use of
StableFuture
s in some cases (mostly relating to stack pinning), unfortunately because of the special caseStableFuture::pin
all examples I have come up with are possible today by just treating the pinned future as aFuture
rather than aStableFuture
, but these changes will allow the same examples to work via a generic stack pinning API once one is defined. One example of the sort of function that is impossible to write without the current special case:This example relies on the forwarding implementation on
Pin
to allow reusing an existing timeout and stream until the timeout expires or a specific message is seen on the stream. This can be trivially changed to use a stack pinning API given one that works in#[async]
context by changing the first two lines and updatingtimeout.as_pin()
toPin::borrow(&mut timeout)
and same for the stream.Obviously this all extends directly to
StableStream
as well.I plan to try making this change this weekend and see if I can find any issues with it. I do feel like removing the
impl<F: Future> StableFuture for F
could be a slight problem in some cases, but unfortunately that collides withimpl<F: StableFuture> StableFuture for Pin<F>
(unless havingStableFuture
defined infutures-core
might actually allow this, I doubt it though).I just saw the async/await and companion RFCs after writing this. I think this is heading more in the direction that those are planning as well, the forwarding implementations are definitely going to be wanted once
Future
is gone.CC @withoutboats
The text was updated successfully, but these errors were encountered: