← На главную

Rust: async-функции не работают без poll и раннера

08.06.2026 14:15 · hackernews

В JavaScript async/await работает «из коробки»: у Node есть встроенный event loop, который сам двигает Promise. В Rust всё иначе — он не поставляет event loop. Вызов async fn просто возвращает Future и не выполняет ни строчки. Тело функции запускается только при вызове poll. poll — единственный метод Future: он спрашивает «можешь ли продвинуться?» и возвращает либо Ready(value) (готово), либо Pending (ждёт). Без poll — никакого прогресса.

Проблема в том, что Future может ссылаться сама на себя (например, хранить указатель на своё же поле). При перемещении в памяти указатель повисает. Для защиты используется Pin<&mut Self> в сигнатуре poll — он гарантирует, что во время опроса Future не сдвинется.

Когда poll возвращает Pending, будущему нужен способ сообщить, что пора опросить снова. Для этого есть Waker, который передаётся внутри Context. Future регистрирует Waker у того ресурса, которого ждёт (сеть, таймер), а когда ответ приходит, Waker вызывается и будит опрашивающий поток.

Автор собирает из этих трёх частей — Future, poll, Waker — простой oneshot-канал вручную. Sender и Receiver разделяют общее состояние Inner через Arc<Mutex<Inner>>. Inner хранит опциональное значение value и опциональный Waker. Sender::send кладёт значение и, если Waker уже оставлен, вызывает waker.wake(). Receiver реализует трейт Future: при опросе проверяет value — если есть, возвращает Ready(value), иначе кладёт клон Waker из контекста и возвращает Pending.

Чтобы это работало, нужен раннер. block_on — простейший вариант: он принимает Future, фиксирует его в памяти через pin!, создаёт Waker, который будит поток через thread::park/unpark, и крутит цикл: poll, если Pending — засыпает, пока Waker не разбудит. Когда приходит Ready, возвращает значение. Это — точка входа из синхронного кода в асинхронный, как block_on в Tokio.

Читать оригинал →