Actually pin_project didn’t take care of it. It just simply require the user to uphold this, so this why I said it is not much better than use explicit unsafe code.
Just giving another example that require manually handle Futures. Here is the alt function: given 2 Future, poll them parallelly, and prepare a result if at least one of them is ready:
pub enum EitherOr<T1,T2> {
This(T1),
That(T2),
Both(T1,T2),
}
pub fn alt<T1, T2>(
f1: impl Future<Output=T1>,
f2: impl Future<Output=T2>
) -> impl Future<Output=EitherOr<T1,T2>> {
struct AltFuture<F1,F2>(F1,F2);
impl<T,F1,F2> Future for AltFuture<F1,F2>
where
F1: Future<Output=T>,
F2: Future<Output=T>,
{
type Output=EitherOr<T1,T2>;
fn poll(self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
let this = unsafe {
let this = self.get_unchecked_mut();
(
Pin::new_unchecked(&mut this.0),
Pin::new_unchecked(&mut this.1),
)
};
match (this.0.poll(lw), this.1.poll(lw)) {
(Ready(v1), Ready(v2)) => EitherOr::Both(v1, v2),
(Ready(v1), _) => EitherOr::This(v1),
(_, Ready(v2)) => EitherOr::That(v2),
(_, _) => Pending,
}
}
}
AltFuture(f1, f2)
}
(Note this is more flexible than the futures::Future::select: it does not require the futures to have the same type. And more importantly, it is less biased: in case that both futures being Ready, it don’t have to pick any arbitrary one.)