Monorepository with Rust: Code Organization in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Monorepository with Rust: Code Organization in Practice

A monorepository (monorepo) is an approach to code management where multiple projects or components are stored in a single repository. In the Rust ecosystem, this practice is especially popular due to the built-in support of Cargo Workspace. In this article, we will explore how to organize a monorepository with Rust in practice: from creating the structure to managing dependencies and testing. You will learn how to avoid common mistakes and make your codebase scalable.



What is Cargo Workspace and Why You Need It

Cargo Workspace is a mechanism that allows you to manage multiple crates (libraries or executables) within a single project. Instead of creating separate repositories for each component, you combine them into a unified structure. This provides several advantages:

  • Unified build system — a single cargo build command builds all crates.
  • Shared dependencies — duplicate dependencies are resolved once, reducing the final binary size.
  • Simplified testing — you can run tests for all crates with a single command.
  • Version consistency — all components use the same versions of libraries.

A monorepository is especially useful for microservices, general-purpose libraries, or projects with a clear separation into layers (e.g., core, API, CLI).



Creating the Monorepository Structure

Let's start with a practical example. Suppose we are developing a task management system. We will have three crates:

  • core — a library with business logic.
  • cli — an executable for the command line.
  • server — a web server (executable).

First, create the root directory and the Cargo.toml file for the workspace:

mkdir task_managercd task_manager# Create the root Cargo.tomltouch Cargo.toml

In the root Cargo.toml, specify the workspace:

[workspace]members = [    "core",    "cli",    "server",]

Now create each crate using cargo new:

cargo new core --libcargo new clicargo new server

After this, the structure will look like this:

task_manager/├── Cargo.toml          # Root workspace├── core/│   ├── Cargo.toml│   └── src/│       └── lib.rs├── cli/│   ├── Cargo.toml│   └── src/│       └── main.rs└── server/    ├── Cargo.toml    └── src/        └── main.rs

Note: the root Cargo.toml does not contain a [package] section, only [workspace]. This is important for correct operation.



Managing Dependencies Between Crates

One of the key tasks in a monorepository is correctly setting up dependencies between crates. In our example, cli and server depend on core. Let's specify this in their Cargo.toml files:

For cli/Cargo.toml:

[package]name = "cli"version = "0.1.0"edition = "2021"

[dependencies]core = { path = "../core" }clap = { version = "4.0", features = ["derive"] } # External dependency for CLI

For server/Cargo.toml:

[package]name = "server"version = "0.1.0"edition = "2021"

[dependencies]core = { path = "../core" }actix-web = "4" # External dependency for web server

In the root Cargo.toml, you can also specify common dependencies if they are needed by all crates, but this is usually done individually. However, the workspace allows resolving version conflicts: if two crates use the same library, Cargo will automatically pull in one version

Blogs

Book Recommendations