Экосистема: serde, clap, tokio и docs.rs
открытый урокЭтот раздел читается без входа. Войди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти
Экосистема: serde, clap, tokio и docs.rs
В std нет ни JSON, ни HTTP, ни даже случайных чисел: всё это живёт в crates.io. Урок-экскурсия по крейтам, которые встречаются почти в каждом проекте, по тем, что ждут тебя дальше в курсе, и по навыку важнее любого списка: выбирать зависимость, которой можно доверять.
Идея
Попроси Python-разработчика скачать JSON и посчитать по нему статистику, и ему хватит стандартной библиотеки: urllib, json, random. Попробуй то же на голой std в Rust и упрёшься на первом шаге: HTTP-клиента нет, JSON-парсера нет, генератора случайных чисел тоже нет.
Это решение, а не бедность. У std самое дорогое обещание в языке: обратная совместимость навсегда. Что попало в стандартную библиотеку, остаётся там до конца, со всеми ошибками дизайна. Python годами тащил в stdlib мёртвые модули и только недавно устроил большую чистку. Rust выбрал другой путь: маленькое ядро со строжайшим отбором, а всё, что ещё ищет лучший дизайн, живёт в crates.io, выпускает версии по semver из прошлого урока и конкурирует за пользователей.
Цена решения: выбирать зависимости теперь твоя работа. Хорошая новость: за десять с лишним лет экосистема устаканилась, и на типовые задачи есть ответы по умолчанию, этот урок их перечисляет. Плохая: ответы меняются, и туториал двухлетней давности может привести тебя в заброшенный крейт. Поэтому вторая половина урока не про список, а про метод: как читать docs.rs и по каким признакам оценивать чужой код. Снимок экосистемы в уроке сделан летом 2026, на Rust 1.96; принципы переживут конкретные версии.
serde: одна модель, много форматов
Один из главных крейтов экосистемы решает одну задачу: превращать структуры Rust в байты и обратно. Главная идея дизайна в том, что serde не знает ни одного формата. Он определяет трейты Serialize и Deserialize плюс derive для них, а каждый формат это отдельный крейт: serde_json, toml и десятки других.
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Config {
name: String,
retries: u32,
#[serde(default)]
verbose: bool,
}
let config: Config = toml::from_str(&text)?; // конфиг из TOML
let json = serde_json::to_string_pretty(&config)?; // та же модель в JSON
Одна структура, и ты получаешь разбор TOML-конфига, JSON для API и любой другой формат без единой новой строчки в модели. Атрибуты #[serde(...)] подкручивают детали: default подставляет значение для пропущенного поля, rename_all = "camelCase" договаривается о стиле имён с JavaScript-миром, deny_unknown_fields превращает опечатку в конфиге в ошибку разбора, а не в молча проигнорированное поле.
Текстовыми форматами дело не заканчивается. Для байтов по проводу есть бинарные крейты: bincode (компактно и быстро), postcard (ещё компактнее, родом из embedded-мира), borsh (детерминированный: один вход даёт ровно одни байты, на нём стоит Solana). Эти имена вернутся в курсе не раз: бинарный протокол сетевой игры и сериализация блоков в блокчейне строятся ровно на этом слое.
clap: CLI из структуры
Второй derive, без которого не обходится почти ни один консольный инструмент:
use std::path::PathBuf;
use clap::Parser;
/// Считает самые частые ошибки в логе
#[derive(Parser)]
struct Args {
/// Путь к файлу лога
path: PathBuf,
/// Сколько строк вывести
#[arg(short, long, default_value_t = 10)]
limit: usize,
}
fn main() {
let args = Args::parse();
// дальше обычный Rust: args.path, args.limit
}
Структура объявляет интерфейс, derive генерирует остальное: разбор аргументов, проверку типов (--limit abc отклонится с внятным сообщением), --help из doc-комментариев, автодополнение для шеллов. Тот же ход, что у serde и thiserror: ты описываешь данные, бойлерплейт пишет макрос.
Рядом с clap целая полка для консольных программ: indicatif рисует прогресс-бары, ratatui строит полноэкранные интерфейсы в терминале, а когда каждая секунда компиляции на счету, есть микро-альтернативы вроде lexopt и pico-args.
Обвязка приложения: ошибки, логи, время
Ошибки ты уже разобрал в уроке про обработку ошибок: thiserror для библиотек, anyhow на границе приложения. Правило слоёв оттуда работает в каждом проекте без изменений.
Логи. Старый крейт log это фасад с пятью макросами уровней, и для библиотек он до сих пор честный минимум. Но выбором для приложений стал tracing: он пишет структурные события и спаны с длительностью, понимает асинхронный код, где один запрос размазан по задачам. Схема такая: код пишет в tracing, приложение в main выбирает подписчика tracing-subscriber и формат вывода.
Время. Долгие годы стандартом был chrono, он жив и сейчас, но подборки вроде blessed.rs первым теперь ставят jiff от автора regex и ripgrep: дизайн по мотивам Temporal из JavaScript, версия формально ещё 0.x, а скачиваний уже за сотню миллионов. Для нового кода смотри сначала на него.
Случайность. rand не в std, и это снова осознанно: у генераторов случайных чисел разные требования, скорость, криптостойкость, воспроизводимость, пусть конкурируют снаружи. Заодно живой пример того, как экосистема двигается: в edition 2024 слово gen стало зарезервированным, и rand переименовал gen() в random(), а thread_rng() в rng(). Туториал с rng.gen() уже не скомпилируется.
И отдельная радость: std понемногу поглощает устоявшееся. Ленивые глобальные значения, ради которых годами тянули lazy_static, а затем once_cell, теперь в стандартной библиотеке: OnceLock с Rust 1.70 и LazyLock с 1.80. Встретил в туториале lazy_static!, считай это маркером возраста: сегодня то же самое пишется без зависимостей.
Туда же, в пояс на каждый день: regex с гарантией линейного времени (никакая строка не подвесит твой сервис катастрофическим перебором), itertools с десятками адаптеров к итераторам, которых не хватает в std, uuid для идентификаторов.
rayon: параллелизм одной строкой
use rayon::prelude::*;
let total: u64 = lines
.par_iter() // было .iter()
.map(|line| cost(line))
.sum();
rayon разбивает работу по ядрам сам: внутри пул потоков и work stealing, снаружи та же цепочка адаптеров, только с приставкой par_. Это data parallelism: одна операция над множеством независимых элементов. Для другой задачи, потоков, которые общаются между собой, есть каналы crossbeam-channel и flume; подробный разговор про них ждёт в блоке о конкурентности.
Async и сеть: tokio и его орбита
От процессора к проводам, и тут главная развилка экосистемы. async fn это часть языка, но кто будет исполнять асинхронные задачи, язык не говорит: рантайм выбираешь ты. Де-факто ответ один: tokio. Планировщик задач, таймеры, асинхронные сокеты и файлы; на нём стоит почти вся сетевая экосистема. Как async устроен изнутри, разберём в отдельном блоке курса, пока достаточно имени.
Вокруг tokio слоями собрана вся сеть. hyper: низкоуровневый HTTP, на котором стоят остальные. tower: композиция сервисов, где таймауты, повторы и лимиты надеваются на обработчик как переиспользуемые обёртки. axum: веб-фреймворк от команды tokio поверх обоих, выбор по умолчанию для нового сервера; жив и его конкурент actix-web. reqwest: HTTP-клиент с человеческим API. А когда async не нужен вовсе, скрипт, CLI, редкие запросы, бери ureq: синхронный клиент без рантайма в нагрузку.
Базы данных. sqlx: асинхронный, без ORM, пишешь честный SQL, а макрос сверяет запросы со схемой настоящей базы прямо на этапе компиляции. diesel: синхронный ORM со строгой типизацией запросов. Для нового async-сервиса сегодня обычно берут sqlx.
И поучительная история вместо эпитафии. Несколько лет у tokio был полноценный конкурент async-std: книга, туториалы, своя экосистема. В марте 2025 проект официально остановлен: в репозитории совет мигрировать на smol, а в базе RUSTSEC появилась запись о неподдерживаемом крейте. Так умирают даже большие зависимости: не взрывом, а тихой записью в базе уязвимостей. Замечать такое заранее и есть навык, которым займёмся через раздел.
Крейты, которые встретятся дальше в курсе
Курс впереди длинный, и почти каждый его блок стоит на паре крейтов. Читай таблицу как трейлер:
| Задача | Крейты | Где в курсе |
|---|---|---|
| Процедурные макросы | syn, quote | блок про макросы |
| Парсеры | nom, winnow | эмулятор, разбор форматов |
| Бинарная сериализация | bincode, postcard | сетевой протокол игры |
| Свойства и бенчмарки | proptest, criterion | везде, где меряем и проверяем |
| WASM | wasm-bindgen, trunk | игры в браузере |
| Криптография | sha2, ed25519-dalek, secp256k1 | подписи и хеши в web3-блоке |
| Ethereum | alloy | web3-блок |
| P2P-сеть | libp2p | узел блокчейна |
| Внедрение зависимостей и реестры | inventory, linkme, ctor | урок про DI и жизнь до main |
| Игровой движок | bevy | урок про DI: ECS как контейнер зависимостей |
Два примечания к таблице, оба про свежесть информации.
Первое: загугли Rust для Ethereum, и найдёшь горы материалов про ethers-rs. Он официально похоронен: разработка остановлена в пользу alloy, на котором теперь стоят Foundry и Reth, главные инструменты ниши. Любой туториал по ethers-rs устарел до того, как ты его открыл.
Второе: похожая судьба у WASM-тулинга. Команда rustwasm распущена в 2025, wasm-bindgen переехал к новым сопровождающим и развивается, а вот wasm-pack остался на общественной поддержке. Сборку Rust-в-браузер сегодня ведут через trunk, им и будем пользоваться.
В парсерах динамика мягче, но того же сорта: nom много лет был ответом по умолчанию и никуда не делся, но активная разработка сместилась в его форк winnow; на нём, например, стоит парсер TOML внутри самого cargo. В уроках встретятся оба имени.
Как читать docs.rs
Каждая версия каждого крейта с crates.io автоматически получает страницу документации на docs.rs. Уметь её читать важнее, чем помнить любой список крейтов, потому что список устареет, а навык нет. Анатомия страницы:
- Корень это doc-комментарий из
lib.rsкрейта: обзор, философия, стартовые примеры. У зрелых крейтов, serde, tokio, clap, корень читается как мини-книга. Начинай с него, а не со списка функций. - Слева дерево модулей, типов и трейтов, сверху поиск. Поиск понимает не только имена, но и сигнатуры: запрос
u64 -> [u8; 8]найдётto_le_bytes. - Кнопка source у каждого элемента ведёт в исходник. Это не пасхалка, а рабочий инструмент: когда докстрока скупа, ответ в коде, и читать исходники экосистемы это нормальная практика, а не взлом.
- Вкладка feature flags показывает, какие фичи есть у крейта и что каждая включает. Половина вопросов вида «почему у меня нет этой функции» решается здесь: функция за фичей, которую ты не включил.
- Переключатель версий сверху. Проверь, что читаешь документацию версии из своего
Cargo.lock, а не latest: API между версиями расходится.
И помни про cargo doc --open: тот же формат собирается локально для твоего проекта вместе со всеми зависимостями, и твой собственный крейт выглядит там так же, как serde. Doc-комментарии из урока про модули окупаются именно тут.
Как выбрать крейт
Сначала карта: подборки blessed.rs и lib.rs отвечают на вопрос «что обычно берут для X» быстрее любого чата. Потом проверка кандидата по признакам:
- Пульс. Дата последнего релиза, ритм релизов, отвечают ли в issues. Годы тишины у живой задачи это сигнал: один из первых веб-фреймворков rocket не выпускал релизов с 2024, и новые серверы на нём не начинают.
- Сопровождающие. Команда (tokio, rust-lang) живучее одиночки, но у одиночек есть репутация: dtolnay (serde, anyhow, thiserror, syn), BurntSushi (regex, ripgrep, jiff), epage (clap, winnow). Имя автора это тоже данные.
- Обратные зависимости. Счётчик dependents на crates.io: крейт, на котором стоят axum и cargo, не исчезнет молча.
- Болезни. База RUSTSEC собирает уязвимости и отметки о брошенных крейтах:
cargo auditсверяет с ней твойCargo.lock, а lib.rs рисует предупреждение прямо на странице крейта. - Вес.
cargo treeпокажет, сколько транзитивных зависимостей приедет следом. Крейт-однострочник с тридцатью зависимостями это плохой обмен.
Отдельный пункт, который недавно показался бы паранойей: цепочка поставки. В 2025 экосистема пережила первую заметную волну атак: фишинговый домен rustfoundation.dev собирал учётки мейнтейнеров crates.io, а крейты-двойники faster_log и async_println, typosquatting от настоящего fast_log, сканировали исходники жертв в поисках приватных ключей Solana и Ethereum. Прицел ровно по аудитории этого курса: web3-разработчик с ключами в проекте. Гигиена простая: имена зависимостей сверяй посимвольно, держи cargo audit или cargo-deny в CI, коммить Cargo.lock (после прошлого урока ты это уже делаешь), а для команд с повышенными требованиями есть cargo-vet, обмен результатами аудита чужого кода.
И обратная сторона, чтобы чек-лист не превратился в фобию: маленький нестандартный крейт это часто нормально. Задача узкая, кода на вечер чтения, зависимостей мало, выкинуть легко: бери, а лучше прочитай целиком, это сильнейшая проверка из возможных. Рисковая зона другая: криптография, unsafe в фундаменте, парсинг недоверенного входа. Там цена чужой ошибки максимальна, и там выбирай только проверенные имена.
ДЗ
Дальше
Зависимости выбраны, теперь сам код. Урок 17 про идиомы, которые отличают код «компилируется» от кода «не разваливается при рефакторинге»: типы вместо bool, исчерпывающий разбор, атрибуты-предохранители и clippy как чек-лист.