Добавить в корзинуПозвонить
Найти в Дзене
Цифровая Переплавка

Когда Future обрывается: почему отмена в async Rust — это и сила, и слабость

Rust подарил нам одну из самых мощных моделей конкурентности — безопасные, компилируемые в состояние конечные автоматы Future. Но вместе с этим он привнёс и новую сложность: отмену (cancellation). В синхронных языках отмена — экзотика, в асинхронном Rust она встроена в саму модель: любое будущее можно отменить простым drop. На RustConf 2025 эта тема снова зазвучала — и не случайно. Масштабные системы на Tokio и async Rust всё чаще упираются в баги, связанные с тем, что будущее исчезает не вовремя, оставляя после себя разрушенные инварианты или потерянные данные. ⚡ Как устроена отмена в async Rust Именно поэтому отмена кажется «естественной», но на деле она слишком лёгкая: достаточно забыть .await или выйти из select! — и часть работы теряется. 🛡️ Две категории безопасности Автор статьи предложил разделять понятия: 💥 Типичные ловушки 🔧 Практические приёмы 💡 Моё мнение Rust сделал невероятное — превратил асинхронность в проверяемую на этапе компиляции систему, где инварианты защищены

Rust подарил нам одну из самых мощных моделей конкурентности — безопасные, компилируемые в состояние конечные автоматы Future. Но вместе с этим он привнёс и новую сложность: отмену (cancellation). В синхронных языках отмена — экзотика, в асинхронном Rust она встроена в саму модель: любое будущее можно отменить простым drop.

На RustConf 2025 эта тема снова зазвучала — и не случайно. Масштабные системы на Tokio и async Rust всё чаще упираются в баги, связанные с тем, что будущее исчезает не вовремя, оставляя после себя разрушенные инварианты или потерянные данные.

Как устроена отмена в async Rust

  • 📦 Future — это всего лишь структура или enum, описывающий состояние. Пока его не poll или не .await, он «мёртвый груз».
  • 🗑️ Отмена — это drop. Если родительский Future уничтожен, то уничтожаются и все дочерние.
  • 🔄 В отличие от JS или Go, где Promise запускается сразу, в Rust будущее пассивно — оно существует, но ничего не делает, пока не будет запущено.

Именно поэтому отмена кажется «естественной», но на деле она слишком лёгкая: достаточно забыть .await или выйти из select! — и часть работы теряется.

🛡️ Две категории безопасности

Автор статьи предложил разделять понятия:

  • Cancel safety — локальное свойство: будущее можно дропнуть без побочных эффектов (например, tokio::time::sleep).
  • 🌍 Cancel correctness — глобальное свойство системы: отмена не нарушает инварианты и не приводит к багам (например, потеря сообщения в tx.send или застрявший Mutex).

💥 Типичные ловушки

  • 🔒 Tokio Mutex. Если в середине критической секции стоит await, и в этот момент произойдёт отмена, данные могут остаться в «грязном» состоянии.
  • 📬 MPSC send. Дроп на полпути — и сообщение безвозвратно теряется.
  • 🧵 select! — красота и проклятие: завершение одного Future мгновенно отменяет остальные.
  • 🤦 try_join! — если один из будущих упал, остальные отменяются, даже если они выполняют важную очистку.

🔧 Практические приёмы

  • 🪄 Перепроектирование API. Вместо send(msg) использовать reserve + send, чтобы сохранить семантику. Для записи — write_all_buf, а не write_all.
  • 📌 Pinning. В select! не пересоздавать будущее каждый раз, а закрепить его и продолжать опрос (сохраняя очередь).
  • 🧵 Задачи вместо future. В Tokio tokio::spawn позволяет довести работу до конца, даже если родитель отвалился. Это ближе к модели JS, где задача живёт сама по себе.
  • 🗂️ Структурированная конкуррентность. Идея: заставлять систему управлять временем жизни задач централизованно, а не полагаться на разрозненные drop.

💡 Моё мнение

Rust сделал невероятное — превратил асинхронность в проверяемую на этапе компиляции систему, где инварианты защищены borrow checker-ом. Но отмена — это та щель, через которую прорывается хаос.

Мне кажется, что будущее (и Future, и буквально 🙂) за новыми языковыми инструментами:

  • async drop, позволяющий гарантировать очистку при отмене;
  • линейные типы, где можно явно указать: «этот future должен быть доведён до конца»;
  • и более широким внедрением структурированной конкуррентности, которая делает отмену предсказуемой.

Пока же инженеры должны помнить: отмена — не баг, а часть модели. И если её не учитывать, проект рискует потерять надёжность.

📎 Источник: