What are the reasons for preferring one over the other between the two bottom examples? Is one more idiomatic than the other?
pin!
Future
before passing it to another Future
that poll
s the passed Future
without any unsafe
code:
use core::{
future::Future,
pin::{pin, Pin},
task::{Context, Poll},
};
use tokio::runtime::Builder;
struct Foo<F> {
x: F,
}
impl<F: Future + Unpin> Future for Foo<F> {
type Output = F::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.x).poll(cx)
}
}
fn main() {
Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async move {
tokio::spawn(foo()).await;
});
}
async fn foo() {
Foo {
x: pin!(<some potentially !Unpin Future>),
}
.await;
}
Pass Future
into another Future
that poll
s the passed Future
by relying on pin projection via pin_project_lite
:
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use tokio::runtime::Builder;
pin_project! {
struct Foo<F> {
#[pin]
x: F,
}
}
impl<F: Future> Future for Foo<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().x.poll(cx)
}
}
fn main() {
Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async move {
tokio::spawn(foo()).await;
});
}
async fn foo() {
Foo {
x: <some potentially !Unpin Future>,
}
.await;
}
I am guessing the former is preferred when pin_project
/pin_project_lite
is not a dependency (direct or indirect) in your crate. I'm guessing the latter is preferred when Foo
is going to be part of a public API since its Future
implementation is more general. What about the situation when pin_project
/pin_project_lite
is already a dependency but Foo
is not part of the public API?