Rust: полезные ссылки
Общая информация
cheats.rs — шпаргалка по Rust начиная от синтактиса и до советов по идиоматичному написанию кода.
blessed.rs — список проверенных временем и массово-используемых библиотек.
lib.rs — как crates.io, но с более адекватной сортировкой и статистикой. Способен также, пусть и довольно криво, показывать похожие по функционалу крейты.
Rust Playground — онлайн-компилятор.
Minimizing Rust Binary Size — советы по уменьшению размера бинарников.
Rust By Example — концепты языка на наглядных примерах.
The Rust Unstable Book — документация по нестабильным фичам языка и компилятора.
Rust Documentation — всё остальное.
Rust Exercises — упражнения и всякие нетривиальные задачки.
Rust Performance Book — документация по оптимизации программ на Rust и алгоритмов в частности.
Rustsec — база данных по уязвимостям в экосистеме.
Интересные проекты
Mustang и Eyra — оба проекта, нацеленных на полное избавление от линковки с libc (динамической и статической), что позволяет создавать полностью самодостаточные бинарники.
Первый использует способ через -Zbuild-std и кастомный таргет, второй через передачу линкеру -nostartfiles.
Примечателен тот факт, что такие программы никак не могут взаимодействовать с аудио- и видеоподсистемами из-за их привязке к glibc dlopen/dlsym.
rustix — безопасные и идиоматичные Rust-интерфейсы к POSIX API. На Linux особенно интересен бэкенд linux_raw: он ходит в ядро напрямую через системные вызовы и вообще не использует libc. В итоге вместо набора сырых RawFd, числовых флагов и указателей получается нормальный типобезопасный Rust API. В общем, довольно близкая к идеалу замена libc для низкоуровневого кода.
relibc — реализация libc на Rust, используемая в Redox OS. В отличие от rustix, который просто даёт удобный Rust API поверх системных вызовов, relibc пытается быть полноценной libc с POSIX- и C-совместимым интерфейсом. Особенно интересно посмотреть на неё в контексте Redox и вообще попыток собрать Unix-подобную систему практически целиком на Rust.
cap-std — API для работы с файлами и другими ресурсами через capability-based подход. Вместо того чтобы дать программе доступ ко всей файловой системе и надеяться, что она не полезет куда не надо, можно сразу передать ей конкретный каталог или файл. Дальше программа физически не сможет обратиться к чему-либо за пределами выданных ей возможностей. Построен поверх низкоуровневых примитивов вроде rustix.
compact_str — String с small string optimization. Короткие строки хранятся прямо внутри самого объекта и поэтому вообще не требуют выделения в куче, а длинные автоматически переходят на обычное выделение памяти. На 64-битных системах CompactString занимает те же 24 байта, что и обычный String, так что его можно использовать практически как прозрачную замену. Есть no_std.
smol_str — ещё один компактный immutable string type, который изначально появился в rust-analyzer. Маленькие строки он хранит прямо внутри себя, поэтому для них не нужна отдельная аллокация, а Clone остаётся O(1) благодаря тому, что данные либо копируются целиком из inline storage, либо просто увеличивается счётчик ссылок. Особенно хорошо подходит для всяких токенов, идентификаторов и прочих строк, которые постоянно клонируются, но не меняются.
heapless — коллекции, которым вообще не нужен куча. Размер Vec, String, Deque, Map и прочих структур задаётся заранее, поэтому они спокойно работают в no_std и embedded-коде. При этом иногда такая модель оказывается удобной и на обычном компьютере, если размер структуры естественным образом ограничен и вам нужны гарантии отсутствия аллокации в куче.
intrusive-collections — коллекции, в которых сами элементы знают, где они находятся. Например, вместо отдельного node, содержащего указатель на объект, нужные ссылки можно встроить прямо в сам объект. Это позволяет избавиться от лишних аллокаций и даёт гораздо больше контроля над layout и временем жизни элементов. Есть интрузивные связанные списки, RB-tree и другие структуры.
linkme — способ собрать одну статическую коллекцию из элементов, разбросанных по разным модулям и даже crates. Каждый модуль просто добавляет свой элемент, а линковщик в итоге собирает всё в один массив. Получается довольно забавный вариант compile-time registry без центрального списка, который пришлось бы вручную обновлять.
inventory — похожая идея для compile-time registries, только с немного другим механизмом. Разные crates могут независимо зарегистрировать какие-нибудь функции, типы или метаданные, а потом программа получает возможность пройтись по всему этому списку. Удобно, когда хочется сделать что-то вроде плагинов или автоматической регистрации обработчиков, но не хочется городить отдельную систему загрузки плагинов.
bumpalo — arena allocator, который жертвует возможностью освобождать отдельные объекты ради скорости и простоты. Все объекты складываются в одну большую арену, а когда они больше не нужны, достаточно освободить её целиком. Особенно удобно для парсеров, AST и вообще любых структур, у которых куча объектов живёт примерно одинаковое время.
linked_list_allocator — простой аллокатор, который хранит свободную память в виде связанного списка свободных блоков. Особенно интересен в no_std-коде: из него можно сделать #[global_allocator] и после инициализации выделять память через обычные Box, Vec и прочие типы из alloc. По сути, один из самых простых способов получить настоящую кучу там, где операционная система тебе его не предоставила.
spin — Mutex, RwLock, Once и прочие примитивы синхронизации, которые вместо сна просто крутят поток в цикле, пока не освободится нужный ресурс. Звучит как плохая идея, но для коротких критических секций и особенно в no_std-окружениях это вполне нормальный инструмент. Никаких системных примитивов вроде futex при этом не требуется.
static_assertions — макросы для проверок, которые хочется сделать во время компиляции. Например, можно проверить размер и выравнивание типа, наличие нужной реализации trait или какое-нибудь отношение между типами. Полезно в основном там, где код завязан на конкретный layout и хочется узнать об ошибке ещё на этапе компиляции, а не после запуска.
io-uring — Rust API для Linux io_uring. В отличие от привычного epoll, здесь можно отправлять в ядро целые очереди операций ввода-вывода и получать результаты обратно через completion queue. Это довольно низкоуровневая штука, которая предоставляет доступ к одному из самых интересных механизмов современного Linux I/O.
Полезные аддоны к Cargo
cargo-about — генерирует список лицензий всех зависимостей проекта. Особенно полезен для проектов, которые нужно распространять вместе с текстами лицензий сторонних библиотек. Настраиваешь шаблон, а cargo about generate собирает из древа зависимостей готовый файл.
cargo-audit — проверяет Cargo.lock на известные уязвимости из вышеупомянутого RustSec Advisory Database. В отличие от обычного обновления зависимостей, он именно ищет опубликованные security advisories и показывает, какая зависимость притащила проблемный crate.
cargo-bloat — показывает, что именно занимает место в готовом бинарнике. Можно быстро выяснить, какой crate или функция раздувает .text, и почему после добавления какой-нибудь зависимости бинарник внезапно вырос на несколько мегабайт. Поддерживает ELF, Mach-O и PE.
cargo-deny — линтер для зависимостей. Помимо уязвимостей он умеет проверять лицензии, запрещать конкретные crates, находить несколько версий одной зависимости и ограничивать источники, откуда вообще разрешено брать зависимости. По сути, cargo-audit на стероидах, если хочется держать древо зависимостей под полным контролем.
cargo-edit — набор команд для редактирования зависимостей прямо из консоли. cargo add и cargo rm уже давно стали частью самого Cargo, поэтому сейчас из оставшегося функционала интереснее всего cargo upgrade: он меняет версии зависимостей именно в Cargo.toml, в отличие от cargo update, который обновляет только Cargo.lock. При этом по-умолчанию обновляется только минорная версия (0.1.0 -> 0.1.1, но не 0.1.1 -> 0.2.0), так что вероятность случайно сломать API минимальна, если автор крейта адекватный.
cargo-expand — показывает, во что Rust превратил код после раскрытия всех макросов и derive. Очень полезно, когда какой-нибудь proc-macro генерирует чёрт знает что и хочется наконец посмотреть, что именно реально попадает в компилятор.
cargo-llvm-cov — обёртка над LLVM source-based code coverage для Cargo. Запускаешь тесты, а на выходе получаешь покрытие кода. Умеет работать с обычными тестами, doctest’ами и другими cargo targets, и выдавать привычные отчёты.
cargo-machete — ищет неиспользуемые зависимости в Cargo.toml. Работает очень быстро, потому что не пытается полноценно компилировать проект, но за эту скорость приходится платить возможными ложными срабатываниями. Хорошая первая проверка перед тем, как тащить за собой десятки уже ненужных crates.
cargo-msrv — ищет минимальную версию Rust, с которой ещё собирается проект. Может не только найти MSRV перебором toolchain’ов, но и проверить уже заявленную в Cargo.toml версию и показать MSRV зависимостей. Для библиотек, которым приходится поддерживать старые toolchain’ы, довольно незаменимая штука.
cargo-semver-checks — проверяет, не сломал ли новый релиз публичный API с точки зрения SemVer. Например, если из библиотеки исчез публичный метод или изменилась сигнатура функции, он найдёт это до cargo publish и объяснит, какое именно изменение считает сломом совместимости. Проверки строятся на rustdoc JSON, поэтому ему не приходится разбирать исходный код самостоятельно.
cargo-show-asm — показывает код, который rustc реально генерирует для конкретной функции. Причём не только assembly, но и LLVM IR, MIR и WASM; умеет также показывать исходник рядом с соответствующим assembly. Отличная штука, когда хочется разобраться, что на самом деле произошло с каким-нибудь #[inline], оптимизацией или очередным «zero-cost abstraction».
cargo-udeps — ещё один поиск неиспользуемых зависимостей, но в отличие от cargo-machete он пытается определить их по результатам компиляции. Поэтому он точнее, но для работы требует nightly Rust. Запускается как cargo +nightly udeps.