23.11 Cargo Workspaces
Workspaces allow you to manage multiple related crates within a single top-level structure. All crates in a workspace share a single target/
directory and a single Cargo.lock
file.
23.11.1 Use Cases
Workspaces are useful for:
- Large Projects: Breaking down a complex application or library into smaller, more manageable internal crates.
- Related Crates: Developing several crates (e.g., a core library, a CLI frontend, a web server) that depend on each other.
- Monorepos: Managing multiple distinct but potentially related projects in one repository.
23.11.2 Setting Up a Workspace
- Create a top-level directory for the workspace.
- Inside it, create a
Cargo.toml
file that defines the workspace members. This file typically doesn’t define a[package]
itself, only the[workspace]
section. - Place the individual crate directories (each with its own
Cargo.toml
) inside the workspace directory or list paths to them.
my_workspace/
├── Cargo.toml # Workspace root manifest
├── member_lib/ # A library crate
│ ├── Cargo.toml
│ └── src/lib.rs
└── member_bin/ # A binary crate using the library
├── Cargo.toml
└── src/main.rs
# Shared target and lock file will appear here after build:
# ├── Cargo.lock
# └── target/
my_workspace/Cargo.toml
:
[workspace]
members = [
"member_lib",
"member_bin",
# You can also use globs: "crates/*"
]
# Optional: Define settings shared across the workspace
[workspace.dependencies]
# Define common dependencies once here
# Example:
# serde = { version = "1.0", features = ["derive"] }
# Member crates can then inherit this:
# serde = { workspace = true, features = ["derive"] } # 'features' overrides if needed
# Optional: Configure dependency resolution strategy
# resolver = "2" # Use the version 2 feature resolver (default since Rust 1.51)
my_workspace/member_bin/Cargo.toml
:
[package]
name = "member_bin"
version = "0.1.0"
edition = "2021"
[dependencies]
# Reference the library crate within the workspace via path or just name
member_lib = { path = "../member_lib" }
# Or if defined in [workspace.dependencies]:
# serde = { workspace = true }
23.11.3 Working with Workspaces
- Cargo commands run from the workspace root operate on all members by default (e.g.,
cargo build
,cargo test
,cargo check
). - Use the
-p <crate_name>
or--package <crate_name>
flag to target a specific member crate:# Build only member_bin cargo build -p member_bin # Run the binary from member_bin cargo run -p member_bin # Test only member_lib cargo test -p member_lib
- Publishing:
cargo publish
run from the root will attempt to publish all publishable members. Use-p
to publish specific members.
23.11.4 Benefits
- Shared Build Cache: Dependencies are compiled only once for the entire workspace.
- Consistent Dependency Versions: A single
Cargo.lock
ensures all crates use the same resolved versions of external dependencies. - Easier Inter-Crate Development: Changes in one crate are immediately available to others in the workspace without needing to publish intermediate versions.
- Atomic Operations: Running tests or checks across the whole project is straightforward.