ПРИМЕЧАНИЕ: данное руководство в настоящее время проходит процесс переписывания после долгого периода без значительных работ. Это работа в процессе, многое отсутствует, а существующее содержимое требует доработки.
Введение
Эта книга является руководством по асинхронному программированию в Rust. Она предназначена для того, чтобы помочь вам сделать первые шаги и узнать больше о продвинутых темах. Мы не предполагаем какого-либо опыта в асинхронном программировании (ни в Rust, ни в другом языке), но мы предполагаем, что вы уже знакомы с Rust. Если вы хотите изучить Rust, вы можете начать с "Языка программирования Rust".
Эта книга состоит из двух основных частей: часть первая — это руководство для начинающих, предназначенное для последовательного чтения, которое проведет вас от полного новичка до среднего уровня. Часть вторая — это набор независимых глав по более сложным темам. Она будет полезна после проработки первой части или если у вас уже есть некоторый опыт с асинхронным Rust.
Вы можете ориентироваться в этой книге несколькими способами:
- Вы можете читать её от начала до конца, по порядку. Это рекомендуемый путь для новичков в асинхронном Rust, по крайней мере для первой части книги.
- Слева на веб-странице находится оглавление.
- Если вам нужна информация по обширной теме, вы можете начать с предметного указателя.
- Если вы хотите найти все обсуждения по конкретной теме, вы можете начать с детального указателя.
- Вы можете проверить, есть ли ответ на ваш вопрос в ЧаВо (Часто задаваемых Вопросах).
Что такое Асинхронное Программирование и зачем оно нужно?
В параллельном программировании программа выполняет несколько действий одновременно (или, по крайней мере, создаёт такое впечатление). Программирование с использованием потоков — это одна из форм параллельного программирования. Код внутри потока написан в последовательном стиле, а операционная система выполняет потоки параллельно. При асинхронном программировании параллелизм полностью осуществляется внутри вашей программы (операционная система не участвует). Асинхронная среда выполнения (которая в Rust является просто ещё одним крейтом) управляет асинхронными задачами совместно с программистом, явно передающим управление с использованием ключевого слова await.
Поскольку операционная система не участвует, переключение контекста в асинхронном мире происходит очень быстро. Более того, асинхронные задачи имеют гораздо меньшие накладные расходы памяти, чем потоки операционной системы. Это делает асинхронное программирование хорошо подходящим для систем, которым необходимо обрабатывать очень много параллельных задач и где эти задачи проводят много времени в ожидании (например, ответов от клиентов или операций ввода-вывода). Это также делает асинхронное программирование хорошим выбором для микроконтроллеров с очень ограниченным объемом памяти и без операционной системы, предоставляющей потоки.
Асинхронное программирование также предоставляет программисту детальный контроль над тем, как задачи выполняются (уровни параллелизма и конкурентности, управление потоком выполнения, планирование и т.д.). Это означает, что асинхронное программирование может быть как выразительным, так и эргономичным для многих применений. В частности, асинхронное программирование в Rust обладает мощной концепцией отмены и поддерживает множество различных вариантов параллелизма (выражаемых с помощью конструкций, включающих spawn и его вариации, join, select, for_each_concurrent и т.д.). Это позволяет создавать компоновываемые и повторно используемые реализации таких концепций, как таймауты, приостановка и регулирование.
Hello, world!
Просто чтобы дать вам представление о том, как выглядит асинхронный Rust, вот пример "hello, world". В нём нет параллелизма, и он не совсем использует преимущества асинхронности. Но он определяет и использует асинхронную функцию и печатает "hello, world!":
// Define an async function. async fn say_hello() { println!("hello, world!"); } #[tokio::main] // Boilerplate which lets us write `async fn main`, we'll explain it later. async fn main() { // Call an async function and await its result. say_hello().await; }
Мы объясним всё подробно позже. Пока обратите внимание, как мы определяем асинхронную функцию с помощью async fn и вызываем её с помощью .await — асинхронная функция в Rust ничего не делает, если её не awaitить1.
Как и все примеры в этой книге, если вы хотите увидеть полный пример (включая Cargo.toml) или запустить его локально, вы можете найти их в репозитории GitHub книги: например, examples/hello-world.
Развитие Асинхронного Rust
Функции асинхронности в Rust разрабатываются уже некоторое время, но это не «завершённая» часть языка. Асинхронный Rust (по крайней мере, части, доступные в стабильном компиляторе и стандартных библиотеках) надежен и производителен. Он используется в production в некоторых из самых требовательных ситуаций в крупнейших технологических компаниях. Однако есть некоторые недостающие части и шероховатости (скорее в смысле эргономики, чем надежности). Вы, вероятно, наткнетесь на некоторые из них во время вашего путешествия по асинхронному Rust. Для большинства недостающих частей существуют обходные пути, и они освещены в этой книге.
В настоящее время большинство пользователей находят шероховатости при работе с асинхронными итераторами (также известными как потоки или streams). Некоторые случаи использования async в трейтах пока плохо поддерживаются. Не существует хорошего решения для асинхронного разрушения (destruction).
Над асинхронным Rust ведется активная работа. Если вы хотите следить за разработкой, вы можете посмотреть на домашнюю страницу Рабочей группы по асинхронности, которая включает их дорожную карту. Или вы можете прочитать цели проекта по асинхронности в рамках Проекта Rust.
Rust — это проект с открытым исходным кодом. Если вы хотите внести свой вклад в разработку асинхронного Rust, начните с документации по внесению вклада в основном репозитории Rust.
-
На самом деле, это плохой пример, потому что
println— это блокирующий ввод-вывод, и обычно это плохая идея — делать блокирующий ввод-вывод в асинхронных функциях. Мы объясним, что такое блокирующий ввод-вывод, в главе TODO и почему вам не следует делать блокирующий ввод-вывод в асинхронной функции, в главе TODO. ↩