Rust Workspaces

In Rust, workspaces are a way to organize multiple packages (crates) into a single project with a shared Cargo.lock
and output directory (target/
). This is especially useful for large projects with multiple components, such as libraries and binaries, that need to share code or build settings.
π§± What Is a Workspace?
A workspace is a set of one or more member crates (packages) managed together by Cargo.
- It has a single root
Cargo.toml
(called the workspace manifest) that defines the workspace. - Each member crate has its own
Cargo.toml
. - All members share the same
Cargo.lock
andtarget/
directory.
π Basic Structure
Hereβs an example of a typical workspace layout:
my_workspace/
βββ Cargo.toml # Workspace root (workspace manifest)
βββ members/
β βββ my_lib/
β β βββ Cargo.toml # A library crate
β βββ my_app/
β βββ Cargo.toml # A binary crate
βββ target/ # Shared build output directory
π οΈ Workspace Root Cargo.toml
[workspace]
members = [
"members/my_lib",
"members/my_app"
]
You can also use globs:
[workspace]
members = ["members/*"]
π¦ Member Crates
Each member crate still has its own Cargo.toml
, like this for my_lib
:
[package]
name = "my_lib"
version = "0.1.0"
edition = "2021"
[dependencies]
And for my_app
:
[package]
name = "my_app"
version = "0.1.0"
edition = "2021"
[dependencies]
my_lib = { path = “../my_lib” } # Use the workspace member
β Benefits of Workspaces
- Single build directory (
target/
) saves build time. - Shared
Cargo.lock
ensures consistent dependency versions across all crates. - Ideal for monorepos: libraries, apps, plugins in one repo.
- Easier testing: you can test the whole workspace with
cargo test
.
π Common Commands
cargo build
(in workspace root) builds all members.cargo build -p my_app
builds just one member.cargo test
,cargo check
, etc. also work similarly.
π Optional Members
You can have crates in your repository that are not part of the workspace using exclude
:
[workspace]
members = ["members/*"]
exclude = ["members/experimental_crate"]
π§ͺ Example Use Case
A real-world example could be:
core
β a reusable library.cli
β a command-line interface binary.web
β a web server binary.tools/gen_docs
β an internal documentation tool.
All living in one repo, built and tested together.