Все мы знакомы с Future комбинаторами и их запутанным кодом. Я могу только представить, как новички реагируют на исходники FutureExt::then
:
trait FutureExt
{
fn then<...>(self, f) -> Then<...>
{
Then::new(self, f)
}
}
impl Then
{
fn poll(self, waker) -> Poll<...> {
self.as_mut().chain().poll(waker, |output, f| f(output))
}
}
impl Chain
{
fn poll(self, waker, f) -> Poll<...>
{
let mut f = Some(f);
// Safe to call `get_unchecked_mut` because we won't move the futures.
let this = unsafe { Pin::get_unchecked_mut(self) };
loop {
let (output, data) = match this {
Chain::First(fut1, data) => {
match unsafe { Pin::new_unchecked(fut1) }.poll(waker) {
Poll::Pending => return Poll::Pending,
Poll::Ready(output) => (output, data.take().unwrap()),
}
}
Chain::Second(fut2) => {
return unsafe { Pin::new_unchecked(fut2) }.poll(waker);
}
Chain::Empty => unreachable!()
};
*this = Chain::Empty; // Drop fut1
let fut2 = (f.take().unwrap())(output, data);
*this = Chain::Second(fut2)
}
}
}
Я понимаю причины, по которым комбинаторы были так написаны. И все же кода в них слишком много, чтобы ухватить суть сходу.
Сравните с переписанным вариантом с использованием async/await синтаксиса:
pub async fn then<...>(future, f) -> ...
{
let future_result = await!(future);
let new_future = f(future_result);
await!(new_future)
}
Я надеюсь мой проект даст вам понимание того, как устроены комбинаторы и как они могли бы быть написаны (цель проекта как fahrenheit для futures executor).
Вот ссылка на проект futures-async-combinators, жду пул реквестов!