23.11 Cargo Workspaces

Workspaces allow multiple packages (crates) to coexist in one directory structure, sharing dependencies and a single lock file. They are compiled, tested, and (optionally) published together. This approach is ideal for:

  • Monorepos: Large projects containing many related crates
  • Shared Libraries: Splitting functionality among crates without overhead
  • Streamlined Builds: Consistent testing and building across multiple crates

23.11.1 Setting Up a Workspace

Suppose you have two crates, crate_a and crate_b, in my_workspace:

my_workspace/
├── Cargo.toml         # Workspace manifest
├── crate_a/
│   ├── Cargo.toml
│   └── src/
│       └── lib.rs
└── crate_b/
    ├── Cargo.toml
    └── src/
        └── main.rs

The top-level Cargo.toml might look like:

[workspace]
members = [
    "crate_a",
    "crate_b",
]

If crate_b depends on crate_a, reference it in crate_b/Cargo.toml via a path:

[dependencies]
crate_a = { path = "../crate_a" }

To build and run:

# Build everything
cargo build

# Build just crate_b
cargo build -p crate_b

# Run the binary from crate_b
cargo run -p crate_b

All crates in the workspace share a single Cargo.lock, ensuring consistent dependency versions.

The command cargo publish publishes the workspaces default members. The default members of a workspace can be set explicitly with the workspace.default-members key in the root manifest. If this is not set, a virtual workspace will include all workspace members. You can also publish single crates of a workspace with:

# Publish only crate_a
cargo publish -p crate_a

23.11.2 Benefits of Workspaces

  • Shared target folder: Avoids duplicate downloads and recompilations.
  • Consistent versions: Common Cargo.lock ensures uniform dependencies.
  • Convenient commands: cargo build, cargo test, and cargo doc can operate on all crates or specific ones.