# Глава 0. Предисловие Добро пожаловать на курс по написанию бэкенда на Rust. За время курса мы пройдём путь от первого «Hello, World!» на Axum до полноценного REST API пиццерии с базой данных, аутентификацией и тестами. ## Для кого этот курс Этот курс для тех, кто уже знаком с основами Rust и теперь хочет перейти от небольших программ и CLI-утилит к написанию REST API. Главное требование — знание основ Rust, а опыт в бэкенде не обязателен. Если вы уже пишете бэкенд на другом языке, например на Go, Python, Node.js, Java или C#, курс покажет, как привычные задачи решаются на Rust и чем этот подход отличается. Если опыта в бэкенде нет, например вы писали CLI-утилиты, эмбеддед, мобильные приложения или только учитесь, курс познакомит вас с бэкенд-разработкой. Её понятия мы будем объяснять по ходу, а базовые знания HTTP и SQL из следующего раздела понадобятся всем. ## Что нужно знать перед началом - Основы Rust из книги [The Rust Programming Language](https://doc.rust-lang.org/book/). - Основы асинхронного Rust из [руководства по Tokio](https://tokio.rs/tokio/tutorial). Tokio — это асинхронный рантайм, то есть библиотека, которая выполняет асинхронный код и берёт на себя работу с сетью и таймерами. На нём построен Axum, с которым мы будем работать в курсе. - HTTP: методы, коды ответов, заголовки, тело запроса. Если есть пробелы, хватит [раздела про HTTP на MDN](https://developer.mozilla.org/ru/docs/Web/HTTP). - Основы SQL: `SELECT`, `JOIN`, `INSERT` и т.д. - Базовая работа с терминалом и Docker. Если Rust — ваш первый язык программирования, советую отложить этот курс на потом. Это не снобизм и не попытка вас отговорить: просто это сложный язык с крутой кривой обучения. Он заставляет держать в голове много непростых понятий: владение и заимствование, времена жизни, трейты и обобщения, алгебраические типы и т.д. Все эти механизмы защищают от ошибок, с которыми вы, скорее всего, ещё не сталкивались. Borrow checker уберегает от многих из них, но чтобы понять, за что благодарить компилятор, нужно хотя бы раз наступить на висячий указатель или изменить список во время итерации по нему. Без такого опыта правила языка выглядят как набор произвольных придирок, а обучение превращается в игру «угадай, куда поставить `&`, чтобы собралось». Начните с Python или Go, напишите на нём что-нибудь работающее, а потом возвращайтесь. ![Жизненный цикл Rust-разработчика: продуктивность сначала резко падает, а затем растёт.](https://raw.githubusercontent.com/senjun-team/senjun-courses/main/illustrations/rust_backend/lifecycle.png) {.illustration} ## Зачем вообще писать бэкенд на Rust ### Строгость Rust проверяет программу строже большинства популярных языков. В нём строгая статическая типизация без неявных приведений типов, нет `null`, а borrow checker следит за владением данными и временем их жизни. Многие ошибки, которые в других языках находят тестами или ловят в проде, в Rust просто не проходят компиляцию. Например, отсутствие значения выражается типом `Option`, и компилятор не даст использовать его как обычное число: ```rust // возвращает None, если данных о товаре нет fn stock_balance(id: u64) -> Option<u32> { ... } fn available_items(id: u64, required: u32) -> u32 { let in_stock = stock_balance(id); // не скомпилируется: Option<u32> и u32 — разные типы std::cmp::min(in_stock, required) } ``` Компилятор укажет на ошибку: ```text error[E0308]: mismatched types --> src/main.rs:8:19 | 8 | std::cmp::min(in_stock, required) | ^^^^^^^^ expected `u32`, | found `Option<u32>` ``` Чтобы код собрался, нужно явно решить, что делать, если данных нет. Например, считать, что товара нет в наличии: ```rust fn available_items(id: u64, required: u32) -> u32 { let in_stock = stock_balance(id).unwrap_or(0); std::cmp::min(in_stock, required) } ``` В Python аналогичный вызов `min(stock_balance(id), required)` запустится без проблем и упадёт с `TypeError`, только когда данных о товаре действительно не окажется, то есть, скорее всего, уже в проде. Rust не делает программу правильной автоматически: логические ошибки никуда не исчезают. Но значительный класс ошибок просто невозможно допустить в коде, который скомпилировался. ### Безопасный рефакторинг Благодаря строгости компилятора рефакторинг перестаёт быть страшным. Вы меняете тип поля в структуре, запускаете `cargo check`, получаете список из сорока мест, исправляете их, и, скорее всего, всё работает. Это свойство чувствуется не на первой неделе, а на шестом месяце жизни проекта, когда кодовая база выросла, а вы уже забыли, как работает половина. ### Отсутствие гонок данных Система типов предоставляет трейты-маркеры [`Send` и `Sync`](https://doc.rust-lang.org/nomicon/send-and-sync.html), которые описывают, какие данные можно передавать между потоками и разделять между ними. Все проверки происходят на этапе компиляции и ничего не стоят во время выполнения. Важно понимать, что речь идёт именно о гонках данных, то есть об одновременном доступе к одной памяти из нескольких потоков, когда хотя бы один из них пишет. Логические гонки, например две параллельные транзакции в базе данных, по-прежнему остаются на вашей совести. ### Предсказуемое потребление ресурсов В Rust нет сборщика мусора, поэтому нет ни внезапных пауз на сборку мусора, ни загадочного роста памяти под нагрузкой. Задержки Rust-сервиса обычно остаются ровно там, где вы их замерили, а не улетают в стратосферу на 95-м перцентиле. ### Один исполняемый файл Итог сборки — один исполняемый файл. Не нужен рантайм в контейнере, не нужен `node_modules`, не нужно виртуальное окружение. ## Где уже используется Rust - Discord [переписал](https://habr.com/ru/articles/487116/) некоторые свои высоконагруженные сервисы с Go на Rust: сборщик мусора Go запускался примерно раз в две минуты и вызывал регулярные скачки задержки. - Cloudflare написала на Rust HTTP-прокси [Pingora](https://blog.cloudflare.com/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet/), который заменил NGINX и обрабатывает более триллиона запросов в день. По данным самой компании, при той же нагрузке он потребляет примерно на 70% меньше процессорного времени и на 67% меньше памяти. - Figma [вынесла](https://www.figma.com/blog/rust-in-production-at-figma/) в отдельный процесс на Rust сериализацию документов — самую требовательную к производительности часть сервера совместного редактирования. Сервер целиком работал на TypeScript, и на больших файлах возникали непредсказуемые скачки задержки. После переноса сериализация стала в 10 раз быстрее. - Dropbox [переписал](https://dropbox.tech/infrastructure/rewriting-the-heart-of-our-sync-engine) с Python на Rust движок синхронизации файлов Nucleus. Одна из причин — возможность выразить сложные инварианты синхронизации в системе типов, чтобы их проверял компилятор. - AWS создала на Rust [Firecracker](https://github.com/firecracker-microvm/firecracker) — технологию виртуализации, на которой работают AWS Lambda и AWS Fargate. - npm [переписал](https://www.rust-lang.org/static/pdfs/Rust-npm-Whitepaper.pdf) на Rust сервис авторизации публикации пакетов, который был узким местом по процессорному времени. Команда сравнивала Node.js, Go и Rust и среди причин выбора Rust называет удобную работу с зависимостями. ## Где уже работает бэкенд на Axum Axum — один из самых популярных веб-фреймворков на Rust, и на нём работают реальные сервисы. Например, на Axum написан бэкенд [crates.io](https://github.com/rust-lang/crates.io) — официального реестра пакетов Rust, через который проходят все зависимости, подключаемые командой `cargo add`. На Axum построен и сервер [Svix](https://github.com/svix/svix-webhooks), сервиса для отправки вебхуков. Ещё один пример — сервер синхронизации истории командной строки [Atuin](https://github.com/atuinsh/atuin). Все эти проекты открыты, поэтому их код можно изучить, чтобы посмотреть, как устроено настоящее приложение на Axum. ## А теперь про производительность Rust быстрый: возможно, вы не раз видели это в бенчмарках, где он стабильно держится в верхней части рейтингов. Но, скорее всего, вам эта скорость не понадобится. Существует целый жанр пет-проектов: высокопроизводительный сервис, способный держать 200 000 запросов в секунду, развёрнут на VPS за 5 долларов, а его суточная аудитория — автор и его мама. В подавляющем большинстве настоящих бэкендов узкое место — не язык, а база данных, сеть, внешнее API и т.д. Переписав сервис с Python на Rust, вы получите более стабильные задержки, высокую производительность и меньшее потребление памяти, но это не имеет смысла, если нагрузка на проект — 20 запросов в день. Поэтому главный аргумент за Rust на бэкенде — не скорость, а надёжность. Компилятор не даёт выстрелить себе в ногу, поэтому работающий сервис реже ломается и его дёшево поддерживать. Производительность — приятный бонус. ## Когда бэкенд на Rust — плохая идея - Скорость разработки. Если Rust для вас не основной язык, писать на нём существенно медленнее, чем на других языках. Разбираться, почему `&str` живёт недостаточно долго и как правильно вернуть ошибку из обработчика, можно долго, и это стоит нервов. С опытом разрыв сокращается и на больших проектах почти исчезает. Например, мне всё равно, на чём писать бэкенд: на FastAPI или на Axum. Но для новичков в языке это может стать серьёзной сложностью. - Время компиляции. Проект средних размеров (пара десятков тысяч строк кода) собирается достаточно долго, тем более на слабом компьютере. Инкрементальная сборка и `cargo check` работают быстрее, но скрипт на Python или проект на Go всё равно запустятся значительно быстрее. Rust — один из самых медленно компилируемых языков программирования. - Найм. Rust-разработчиков на рынке немного, а вакансий на рынке РФ ещё меньше. Поэтому, если вы изучаете Rust в первую очередь ради поиска работы, это не лучшая идея. - Незрелость части экосистемы. Библиотек меньше, чем в Python или JS. SDK для вашего вендора может не существовать, и тогда придётся обращаться к его REST API вручную. Удобных библиотек-обёрток уровня FastStream из Python нет или они слишком незрелы, поэтому придётся писать свои костыли. Общее правило: Rust хорош там, где код будет жить долго, стоимость ошибки в проде высокая, а нагрузка постоянная. Rust плох там, где требования меняются каждый день, а проект может быть выброшен через месяц. ## Что будет в курсе 1. Введение в [Axum](https://docs.rs/axum/). Что это за фреймворк и чем он отличается от альтернатив, первый работающий сервер. Работа с JSON, маршрутизация запросов, обработчики и ответы, извлечение данных из запроса, состояние приложения, обработка ошибок, настройка проекта. 2. Работа с базой данных. PostgreSQL и [sqlx](https://docs.rs/sqlx/): пул соединений, миграции, проверка запросов на этапе компиляции, транзакции, репозитории, преобразование ошибок базы данных в HTTP-ответы и пагинация. 3. Аутентификация и авторизация. Хеширование паролей, сессии и JWT, refresh-токены, защита маршрутов. 4. Продвинутые темы. Промежуточные слои (middleware), логирование, отладка, тестирование, загрузка файлов, WebSocket, SSE, фоновые задачи, корректное завершение работы сервера, шаблоны и т.д. 5. Мини-проект пиццерии. Собираем полноценное приложение на основе полученных знаний. ## Чего в курсе не будет - Обучения Rust с нуля. - Фронтенда: API будем проверять через curl и Postman. - Микросервисов, gRPC, Kubernetes и прочей распределённой архитектуры. - Глубин асинхронного Rust: ручной реализации `Future`, устройства планировщика Tokio, `Pin` и `unsafe`. ## Окружение - Компилятор Rust, установленный по официальной [документации](https://rust-lang.org/tools/install/). - Редактор: VSCode, RustRover, Neovim, Zed или любой другой с поддержкой [rust-analyzer](https://rust-analyzer.github.io/). - curl или Postman для отправки HTTP-запросов. - Docker для запуска PostgreSQL во втором модуле. На Windows удобнее работать через WSL.
Наша группа в telegram. Задавайте вопросы и общайтесь.
Задонатить. Поддержите развитие площадки.