Перейти к основному содержимому

Cargo — workspace, features и профили

Разработчику Архитектору

Cargo — workspace, features и профили

Зачем углубляться в Cargo

После первой программы вы уже знаете cargo new и cargo run. Реальные проекты редко укладываются в один файл: появляются библиотека + бинарник, несколько crate в одном репозитории, опциональные модули (features), разные режимы оптимизации (profiles), иногда скрипт build.rs перед компиляцией.

Cargo — единая точка: зависимости, сборка, тесты, документация. Понимание манифеста Cargo.toml экономит часы при отладке «почему не собралось на CI».

Обзор инструментов: фреймворки и Cargo. Синтаксис: справочник.


Словарь

ТерминЗначение
Package (пакет)То, что описано одним Cargo.toml — имя, версия, зависимости.
CrateЕдиница компиляции: библиотека (rlib) или исполняемый файл.
WorkspaceНесколько пакетов в одном репозитории с общим Cargo.lock.
FeatureФлаг условной компиляции и опциональных зависимостей.
ProfileНабор настроек компилятора: dev, release, test.
Cargo.lockЗафиксированные точные версии зависимостей (для приложений коммитят в git).

Один пакет — бинарник и библиотека вместе

Типичный сервис:

my-app/
Cargo.toml
src/
main.rs # точка входа: cargo run
lib.rs # логика, pub API, тесты

Cargo.toml:

[package]
name = "my-app"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1", features = ["derive"] }

[[bin]]
name = "my-app"
path = "src/main.rs"

По соглашению Cargo и так ищет src/main.rs и src/lib.rs; секция [[bin]] нужна, если бинарников несколько или путь нестандартный.

Практика: вся логика в lib.rs (pub fn run()), main.rs только вызывает my_app::run(). Тогда интеграционные тесты в tests/ подключают библиотеку как внешний клиент.

Для динамической библиотеки под FFI:

[lib]
crate-type = ["cdylib"]

Собирается .dll / .so / .dylib — см. FFI.


Workspace (монорепозиторий)

Несколько связанных crate в одном git-репозитории:

acme/
Cargo.toml # корень: [workspace]
crates/
core/
Cargo.toml
src/lib.rs
api/
Cargo.toml
src/main.rs

Корневой Cargo.toml:

[workspace]
resolver = "2"
members = [
"crates/core",
"crates/api",
]

[workspace.package]
edition = "2021"
version = "0.1.0"

[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
  • resolver = "2" — современные правила разрешения features зависимостей (рекомендуется для новых workspace).
  • [workspace.dependencies] — общий каталог версий; в дочерних пакетах пишут serde = { workspace = true }.

crates/api/Cargo.toml:

[package]
name = "acme-api"

[dependencies]
acme-core = { path = "../core" }
tokio = { workspace = true }
serde = { workspace = true }

Команды из корня репозитория:

cargo build -p acme-api
cargo test --workspace
cargo run -p acme-api

Преимущества: один Cargo.lock, переиспользование кода через path = "..." без публикации на crates.io, согласованные версии tokio / serde во всех crate.


Features (условная сборка)

Feature — имя переключателя в Cargo.toml. Он может включать зависимость и куски кода через #[cfg(feature = "...")].

[dependencies]
reqwest = { version = "0.12", optional = true }

[features]
default = ["http"]
http = ["dep:reqwest"]
  • optional = true — зависимость тянется только если включена feature.
  • default — что включено при обычном cargo build.
  • http = ["dep:reqwest"] — синтаксис Cargo 1.60+: явная привязка feature к зависимости.

В коде:

#[cfg(feature = "http")]
pub fn fetch(url: &str) -> Result<String, reqwest::Error> {
reqwest::blocking::get(url)?.text()
}

Сборка:

cargo build --no-default-features
cargo build --features http
cargo test --all-features

Важно для дизайна: features должны быть аддитивными (включение не ломает чужой код). Другой crate в графе зависимостей может снова включить вашу feature — полагаться на «мы её выключили» нельзя. Взаимоисключающие режимы лучше разнести по разным crate или документировать один главный feature.


Профили — dev, release, test

Профиль — набор флагов компилятора в Cargo.toml:

[profile.dev]
opt-level = 0
debug = true

[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
КомандаПрофильНазначение
cargo builddevБыстрая итерация, много отладочной информации
cargo build --releasereleaseПродакшен: оптимизация, меньший бинарник
cargo testtestКак dev + отладка для тестов
  • opt-level — степень оптимизации (0 — почти без оптимизаций).
  • lto — link-time optimization, дольше сборка, иногда меньше и быстрее бинарник.
  • codegen-units = 1 в release — одна единица генерации кода, лучше оптимизация, дольше компиляция.

Кастомный профиль для бенчмарков:

[profile.bench]
inherits = "release"
debug = true

Зависимости — секции и версии

[dependencies]
anyhow = "1"
thiserror = "2"

[dev-dependencies]
tokio = { version = "1", features = ["rt", "macros"] }

[build-dependencies]
cc = "1"
СекцияКогда используется
dependenciesОсновной код
dev-dependenciesТолько тесты, примеры, бенчмарки
build-dependenciesТолько скрипт build.rs

Версия "1" на crates.io означает semver: совместимы обновления 1.x.y, но не 2.0.0. cargo update пересчитывает Cargo.lock. Для библиотек, публикуемых на crates.io, lock-файл обычно не коммитят; для приложений — коммитят, чтобы CI и коллеги собирали одинаково.

Совет: у тяжёлых зависимостей отключайте лишние default-features — ускоряет сборку у всех, кто подключит ваш crate.


build.rs — скрипт до компиляции

Файл build.rs в корне пакета Cargo запускает перед компиляцией Rust-кода. Типичные задачи:

  • собрать C/C++ через cc;
  • сгенерировать код (bindgen, protobuf);
  • передать линковщику пути: println!("cargo:rustc-link-search=...");
  • сказать Cargo пересобрать при изменении файла: println!("cargo:rerun-if-changed=...").

Пример:

// build.rs
fn main() {
cc::Build::new()
.file("native/add.c")
.compile("native_add");

println!("cargo:rerun-if-changed=native/add.c");
}

Подробнее про связывание с C — FFI на практике.


Полезные команды

cargo tree -p my-app # дерево зависимостей
cargo check # проверка типов без полной линковки (быстрее build)
cargo doc --open # локальная документация зависимостей и вашего кода
cargo clippy -- -D warnings # линтер
cargo fmt # единый стиль форматирования

Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).