Как Rust управляет зависимостями с помощью Cargo и что такое workspace в Cargo?

В Rust система управления зависимостями и сборкой проектов реализована через инструмент под названием Cargo. Это официальный менеджер пакетов и сборщик проектов, аналог npm в JavaScript, pip в Python или Maven в Java. Он выполняет следующие функции:

  • инициализация проектов;

  • управление зависимостями;

  • компиляция кода и зависимостей;

  • запуск и тестирование кода;

  • публикация библиотек на crates.io.

Как Cargo управляет зависимостями

В корне каждого проекта на Rust, созданного через Cargo, находится файл Cargo.toml — это манифест проекта, где описаны:

  • метаданные пакета (название, версия, авторы);

  • зависимости и их версии;

  • информация о сборке, фичах, профилях, скриптах сборки и т.д.

Пример секции зависимостей:

\[dependencies\]
serde = "1.0"
rand = "0.8"

Версионирование

Cargo использует семантическое версионирование (semver). Например, запись serde = "1.0" означает:

  • можно использовать версию >= 1.0.0 и < 2.0.0;

  • если опубликована 1.0.159, Cargo установит самую новую совместимую версию.

Также можно указывать более строго:

  • \=1.0.104 — точно эта версия;

  • >=1.0, <1.1 — вручную указанный диапазон;

  • ~1.0.4 — совместимость с патчами, но не с минорными изменениями;

  • * — любая версия (не рекомендуется).

Cargo сохраняет все зависимости в файл Cargo.lock, чтобы обеспечить детерминированную сборку — каждый участник проекта использует точно те же версии библиотек.

Зависимости из других источников

Cargo поддерживает несколько источников:

  • crates.io — основной официальный реестр;
  • git — можно указать URL Git-репозитория:
mylib = { git = "https://github.com/user/mylib" }
  • локальные пути — для работы с зависимостями в соседних директориях:
mylib = { path = "../mylib" }

Cargo сам загружает зависимости, компилирует их в бинарную или библиотечную форму и кэширует в директории ~/.cargo/registry и target.

Что такое workspace в Cargo

Workspace в Cargo — это способ объединения нескольких пакетов (crate’ов) в одну общую структуру проекта. Он позволяет:

  • совместно использовать зависимости;

  • компилировать и тестировать все пакеты сразу;

  • настраивать общий Cargo.lock (общая фиксация зависимостей);

  • управлять сложными проектами, где есть основное приложение и вспомогательные библиотеки;

  • оптимизировать сборку (более точное кеширование, меньше пересборок).

Пример структуры workspace

my_project/
├── Cargo.toml # определяет workspace
├── Cargo.lock
├── app/ # первый crate
 └── Cargo.toml
├── lib/ # второй crate
 └── Cargo.toml
└── utils/ # третий crate
└── Cargo.toml

Файл Cargo.toml в корне:

\[workspace\]
members = \[
"app",
"lib",
"utils"
\]

Каждый из app, lib, utils имеет собственный Cargo.toml, но не имеет своего Cargo.lock — он общий для всего workspace и находится в корне.

В чём преимущества workspace

  1. Совместное кэширование и сборка: повторно используются артефакты между проектами.

  2. Масштабируемость: удобно для монорепозиториев, разделения логики на модули.

  3. Разделение ответственности: можно компилировать только нужные пакеты через флаг --package.

Использование внутри workspace

Если один crate зависит от другого внутри workspace, можно указать зависимость через path, не публикуя crate на crates.io:

\[dependencies\]
my_utils = { path = "../utils" }

Cargo понимает, что utils — это часть workspace, и корректно разрешает зависимости.

Таким образом, Cargo не только предоставляет простой способ управления зависимостями, но и делает Rust-проекты удобными для масштабирования и поддержки за счёт концепции workspace. Это особенно полезно при разработке библиотек, модульных CLI-инструментов, сервисов и сложных бизнес-приложений.