Как стать автором
Обновить
590.8
OTUS
Цифровые навыки от ведущих экспертов

Как работает Cargo

Уровень сложностиПростой
Время на прочтение6 мин
Количество просмотров2.5K

Привет, Хабр!

На дворе 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, происходит следующее:

  1. Cargo читает Cargo.toml + Cargo.lock.

  2. Скачивает все зависимости (если ещё не в кеше).

  3. Кладёт исходники зависимостей в ~/.cargo/registry/src/.

  4. Компилирует каждый dependency в виде .rlib, .d, .rmeta и так далее.

  5. Кладёт всё в 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 borrow foo as 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 и его возможностях, приглашаем на серию открытых уроков, где вы сможете разобраться в ключевых аспектах языка и применении его на практике. Записывайтесь по ссылкам ниже:

Теги:
Хабы:
+10
Комментарии22

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS