Трейт Future
#![allow(unused)] fn main() { pub trait Future { type Output; // Обязательный метод fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; } }
Фьючер представляет асинхронное вычисление, обычно получаемое с помощью async.
Фьючер - это значение, которое, возможно, еще не завершило вычисление. Такой тип "асинхронного значения" позволяет потоку продолжать выполнять полезную работу, пока он ожидает доступности значения.
Метод poll
Основной метод фьючера, poll, пытается разрешить фьючер в конечное значение. Этот метод не блокирует выполнение, если значение еще не готово. Вместо этого текущая задача планируется к пробуждению, когда можно будет продолжить прогресс путем повторного опроса. Контекст, передаваемый в метод poll, может предоставить Waker - дескриптор для пробуждения текущей задачи.
При работе с фьючером вы обычно не вызываете poll напрямую, а используете .await для получения значения.
Обязательные ассоциированные типы
type Output
Тип значения, производимого при завершении.
Обязательные методы
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>
Пытается разрешить фьючер в конечное значение, регистрируя текущую задачу для пробуждения, если значение еще не доступно.
Возвращаемое значение
Эта функция возвращает:
Poll::Pendingесли фьючер еще не готовPoll::Ready(val)с результатомvalэтого фьючера, если он успешно завершился
После того как фьючер завершился, клиенты не должны опрашивать его снова.
Когда фьючер еще не готов, poll возвращает Poll::Pending и сохраняет клон Waker, скопированный из текущего Context. Этот Waker затем будет разбужен, как только фьючер сможет продолжить выполнение. Например, фьючер, ожидающий доступности сокета для чтения, вызовет .clone() на Waker и сохранит его. Когда в другом месте поступает сигнал, указывающий, что сокет доступен для чтения, вызывается Waker::wake и задача сокетного фьючера пробуждается. После пробуждения задачи она должна снова попытаться опросить фьючер, что может привести (или не привести) к получению конечного значения.
Важно отметить, что при многократных вызовах poll только Waker из Context, переданного в последний вызов, должен быть запланирован для получения пробуждения.
Характеристики времени выполнения
Сами по себе фьючеры инертны; они должны активно опрашиваться, чтобы базовое вычисление могло прогрессировать. Это означает, что каждый раз при пробуждении текущей задачи она должна активно переопрашивать ожидающие фьючеры, которые все еще представляют для нее интерес.
Тем не менее, некоторые фьючеры могут представлять значение, которое вычисляется в другой задаче. В этом случае базовое вычисление фьючера просто выступает в качестве канала для значения, вычисляемого этой другой задачей, которая будет выполняться независимо от фьючера. Фьючеры такого типа обычно получаются при порождении новой задачи в асинхронной среде выполнения.
Функция poll не должна вызываться повторно в тесном цикле - вместо этого она должна вызываться только тогда, когда фьючер указывает, что готов к прогрессу (путем вызова wake()). Если вы знакомы с системными вызовами poll(2) или select(2) в Unix, стоит отметить, что фьючеры обычно не страдают от тех же проблем "все пробуждения должны опрашивать все события"; они больше похожи на epoll(4).
Реализация poll должна стремиться возвращаться быстро и не должна блокировать. Быстрое возвращение предотвращает ненужную загрузку потоков или циклов событий. Если заранее известно, что вызов poll может занять много времени, работу следует выгрузить в пул потоков (или что-то подобное), чтобы обеспечить быстрое возвращение из poll.
Паника
После того как фьючер завершился (вернул Ready из poll), повторный вызов его метода poll может вызвать панику, заблокировать выполнение навсегда или вызвать другие виды проблем; трейт Future не накладывает требований на последствия такого вызова. Однако, поскольку метод poll не помечен как unsafe, применяются обычные правила Rust: вызовы никогда не должны вызывать неопределенное поведение (повреждение памяти, неправильное использование небезопасных функций и т.п.), независимо от состояния фьючера.
Реализаторы
#![allow(unused)] fn main() { impl<F> Future for &mut F where F: Future + Unpin + ?Sized, type Output = <F as Future>::Output }
#![allow(unused)] fn main() { impl<F> Future for AssertUnwindSafe<F> where F: Future, type Output = <F as Future>::Output }
#![allow(unused)] fn main() { impl<F, A> Future for Box<F, A> where F: Future + Unpin + ?Sized, A: Allocator, type Output = <F as Future>::Output }
#![allow(unused)] fn main() { impl<P> Future for Pin<P> where P: DerefMut, <P as Deref>::Target: Future, type Output = <F as Future>::Output }
#![allow(unused)] fn main() { impl<T> Future for Exclusive<T> where T: Future + ?Sized, type Output = <F as Future>::Output }
#![allow(unused)] fn main() { impl<T> Future for Pending<T> type Output = T }
#![allow(unused)] fn main() { impl<T> Future for Ready<T> type Output = T }
#![allow(unused)] fn main() { impl<T, F> Future for PollFn<F> where F: FnMut(&mut Context<'_>) -> Poll<T>, type Output = T }