This post describes how I set up Cargo workspaces.
A workspace is composed of smaller crates that are all compiled together. This is useful for large projects that have clear boundaries between components.
It also makes it possible to publish crates to crates.io separately!
The Recipe
The folder structure looks like this:
workspace
├── common
├── server
└── client
We create a crate for each of the components of our project.
We will see how the server
and client
crates share code by using the common
crate & define shared dependencies in the workspace so that the same version is used in all of the crates.
How to do it
This section describes how to bootstrap a workspace.
Create the workspace
Our project will be called bazinga
.
We will pretend that this project implements a client and server that share some code.
cargo new bazinga
rm -rf bazinga/src
We remove the src
folder because we will be using a workspace, not an application or library.
Create the crates
Create the child crates within the workspace:
cd bazinga
cargo new server
cargo new client
cargo new common
Add workspace members
[workspace]
members = [
"server",
"client",
"common",
]
Connect the crates
We need to add the common
crate as a dependency to the server
and client
crates.
In both server/Cargo.toml
and client/Cargo.toml
:
[dependencies]
common = { path = "../common" }
To verify that it is set up properly, run cargo check
in the workspace root.
The common
namespace is now available in both the server
and client
crates.
Define dependencies
Normally you would specify versions for dependencies that you use in the Cargo.toml
file of the crate that uses them.
However, if you want to use the same version of a dependency in multiple crates, you can specify it in the workspace Cargo.toml
file.
See inheriting a dependency from a workspace for details on how to share a dependency between crates.
Workspace
You will define workspace.dependencies and pull them into the crates that need them.
In workspace/Cargo.toml
:
[workspace]
members = [
"server",
"client",
"common",
]
[workspace.dependencies]
tokio = { version = "1.26.0", features = ["full"] }
Workspace members
In the crates which inherit dependencies from the workspace, you will specify a dependency using the syntax <dependency>.workspace = true
.
For example, in server/Cargo.toml
:
[package]
name = "server"
version = "0.1.0"
edition = "2021"
[dependencies]
common = { path = "../common" }
tokio.workspace = true
Run cargo check
again in the workspace root to verify that it is set up properly.
Conclusion
We’ve seen how to set up a workspace with multiple crates.
Library code can be shared between the workspace members.
It is possible to define a dependency’s version once in the workspace and then inherit it in the crates that need it.