Привет, Хабр!
На дворе 2025, и у каждого языка свой подход к сборке, зависимостям и публикации. В Rust за это отвечает Cargo — инструмент, который берёт на себя всё: от менеджмента зависимостей до тестов, бенчмарков и выкладки на crates.io.
И вот это мы и рассмотрим в статье: как устроен Cargo изнутри, зачем нужен Cargo.toml, как подключать зависимости, куда падают артефакты сборки, что делает cargo check, как запускать и бенчмаркать, и как наконец создать свой крейт на crates.io.
Что такое Cargo и зачем тебе Cargo.toml
Cargo — это не просто «сборщик» или «менеджер зависимостей». Это единая точка входа в Rust‑проект: от инициализации и сборки — до тестов, бенчей, зависимостей и публикации.
Он появился не «потому что надо как у pip/npm» — а потому что в Rust иначе нельзя. Язык строго типизирован, компилируемый, с сильной моделью зависимостей и zero‑cost‑абстракциями. Без этого инструмента разработка на Rust быстро превращалась бы в болото из rustc‑флагов, ручного подключения крейтов, проблем с совместимостью и нестабильных билдов. Именно поэтому Cargo стал частью экосистемы с самого начала — не опциональной, а встроенной.
Можно, конечно, скомпилить .rs вручную через rustc, но это как запускать Kubernetes через bash‑скрипты. Cargo стандартизирует:
-
структуру проекта;
-
формат манифеста;
-
процесс сборки;
-
работу с зависимостями;
-
тестирование, бенчмарки и документацию;
-
публикацию на crates.io.
И главное — он делает это консенсусно. Все Rust‑проекты выглядят одинаково, собираются одинаково и ведут себя одинаково.
Файл Cargo.toml — это ядро проекта. Это не просто файл с зависимостями, как requirements.txt или package.json. Это декларативный API проекта, через который Cargo понимает:
-
как его зовут,
-
какой у него edition,
-
какие зависимости он использует (в том числе dev, build, optional),
-
какие features включены,
-
где исходники,
-
какие есть бинарники,
-
что класть в публикацию и т. д.
И именно Cargo.toml позволяет создавать то, что в других языках потребовало бы 10 конфигов, 3 генератора и один build.rs.
Допустим, у нас есть проект. Пишем на Rust. У нас обязан быть Cargo.toml в корне. Это manifest:
[package] name = "my_cool_app" version = "0.1.0" edition = "2021" [dependencies] serde = { version = "1.0", features = ["derive"] }
Здесь объявляется всё:
-
имя крейта,
-
версия,
-
Rust edition (
2015,2018,2021— сейчас это важно, особенно для async/await), -
зависимости и их фичи.
Хочешь dev‑зависимости (тесты, бенчи)? Просто добавь:
[dev-dependencies] criterion = "0.5"
Нужна сборка на этапе компиляции?
[build-dependencies] cc = "1.0"
Нужен workspace из нескольких крейтов:
[workspace] members = [ "core-lib", "cli", "server", ]
И никаких requirements.txt, setup.py, package.json — всё в одном файле.
Несколько полезных крейтов:
-
serde/serde_json— сериализация/десериализация в любые форматы (JSON, TOML, YAML и т. д.). -
anyhow— удобная работа с ошибками без драмы, с контекстами и backtrace. -
thiserror— макросы для описания собственных ошибок без боли. -
log+env_logger/tracing— логирование. Первый — классика, второй — современный и async‑friendly. -
reqwest— полноценный HTTP‑клиент на базеhyper. -
tokio/async-std— async runtime’ы. Если нужен асинхрон, без них никуда. -
clap/argh— для CLI‑приложений, парсинг аргументов без шаманства. -
rayon— параллельные итераторы: хочешь использовать все ядра без ручногоthread::spawn? Вот оно. -
regex— регулярки, быстрые и удобные. -
crossbeam— адекватные примитивы многопоточности. -
lazy_static/once_cell— глобальные константы и ленивая инициализация. -
dashmap— конкурентныйHashMap. -
uuid— генерация UUID’ов всех форматов.
Как подключать зависимости правильно
Cargo работает с crates.io по умолчанию. Указал имя и версию — и поехали:
[dependencies] chrono = "0.4"
Нужен git?
serde_json = { git = "https://github.com/serde-rs/json", branch = "main" }
Локально?
my_utils = { path = "../utils" }
Нужно включить только определённые фичи?
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
Мы сами решаем, как строится граф зависимостей. Cargo сам скачает, прокаеширует, откомпилит, положит в ~/.cargo и target/.
Как устроен процесс сборки: .cargo, target/, кеш, артефакты
Когда мы вызываем cargo build, происходит следующее:
-
Cargo читает
Cargo.toml+Cargo.lock. -
Скачивает все зависимости (если ещё не в кеше).
-
Кладёт исходники зависимостей в
~/.cargo/registry/src/. -
Компилирует каждый dependency в виде
.rlib,.d,.rmetaи так далее. -
Кладёт всё в
target/debug/deps/.
Где и что хранится?
project/ ├── Cargo.toml ├── Cargo.lock ├── src/ │ └── main.rs └── target/ ├── debug/ │ ├── deps/ # Все .rlib и бинарники │ └── build/ # Вывод сборки build.rs ├── release/ # Оптимизированный билд └── incremental/ # Кеш для повторной сборки
Бинарник будет здесь:
target/debug/my_cool_app
А если:
cargo build --release
То здесь:
target/release/my_cool_app
cargo check
Запускает все фазы компиляции до линковки. То есть:
-
Проверяет синтаксис — как
rustc, но быстрее. -
Запускает анализ типов — включая вывод типов (
let x = ...без аннотации? не проблема). -
Гоняет
borrow checker— чтобы сразу можно было увидеть все «cannot borrowfooas mutable» и прочие прелести. -
Выполняет разбор lifetime’ов — чтобы не втыкать потом, где
'aконфликтует с'static.
Но: он не создаёт бинарник, не линкует и не тратит время на всякое лишнее.
Когда его использовать?
Всегда, когда:
-
поменял тип поля в структуре — и хочется проверить, где ещё всё отвалится;
-
рефакторишь сигнатуру функции;
-
быстро фиксим compile‑time ошибки;
-
хочется мгновенную обратную связь на каждом сохранении (он почти как
tsc --noEmitдля Rust).
cargo check
Результат:
Checking my_cool_app v0.1.0 (/home/me/projects/my_cool_app) error[E0308]: mismatched types --> src/main.rs:7:20 | 7 | let name: i32 = "Ivan"; | ^^^^^^^^ expected `i32`, found `&str`
И бонус: работает с features, таргетами и workspace’ами
Хочется проверить только с определёнными фичами:
cargo check --features "tls async"
Проверка для релизной сборки:
cargo check --release
Таргет на ARM:
cargo check --target armv7-unknown-linux-gnueabihf
cargo run: как запускать с аргументами и в разных окружениях
Команда:
cargo run -- --config ./config.yml
Выполняется как:
cargo build ./target/debug/my_cool_app --config ./config.yml
Можно:
запускать конкретный бинарь (если их несколько):
cargo run --bin server
запускать с фичами:
cargo run --features "use_tls"
запускать для другого таргета (например, armv7):
cargo run --target armv7-unknown-linux-gnueabihf
cargo bench
В стандартной библиотеке есть поддержка бенчей, но она требует nightly. Пример:
#![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_sum(b: &mut Bencher) { let data: Vec<u64> = (1..1_000_000).collect(); b.iter(|| data.iter().sum::<u64>()); }
Если не любим nightly, берем criterion.rs:
[dev-dependencies] criterion = "0.5"
use criterion::{criterion_group, criterion_main, Criterion}; fn bench_add(c: &mut Criterion) { c.bench_function("sum 1..1000", |b| { b.iter(|| (1..1000).sum::<u64>()); }); } criterion_group!(benches, bench_add); criterion_main!(benches);
Запуск:
cargo bench
Как написать свой крейт и выложить его на crates.io
Зарегистрироваться
Переходим на crates.io. Логинимся через GitHub. Переходм в /me → генерируем API Token
cargo login <токен>
Cargo.toml
[package] name = "fastmath" version = "0.1.0" edition = "2021" description = "Fast math utilities for Rust" license = "MIT OR Apache-2.0" repository = "https://github.com/user/fastmath" readme = "README.md" keywords = ["math", "fast", "utils"] categories = ["algorithms"]
Добавляем README.md и LICENSE
touch README.md echo "MIT" > LICENSE
Проверка и публикация
cargo package # Проверит, что всё ок cargo publish # И только потом выкладывай
После этого крейт доступен вот так:
[dependencies] fastmath = "0.1"
Заключение
У других языков свои инструменты. У Rust — Cargo. Он простой, мощный и делает всё, что нужно: зависимости, сборка, тесты, бенчи, публикация.
А если вы ещё не писали свои крейты — самое время начать.
Если вы хотите углубить свои знания о Rust и его возможностях, приглашаем на серию открытых уроков, где вы сможете разобраться в ключевых аспектах языка и применении его на практике. Записывайтесь по ссылкам ниже:
-
23 апреля — Обработка ошибок. Оператор «?». Обработка Result и Option
-
6 мая — Пишем нейросеть на Rust с нуля
-
22 мая — За что разработчики любят Rust
ссылка на оригинал статьи https://habr.com/ru/articles/902584/
Добавить комментарий