Cargo Workspaces
What happens when the program keep growing and have multiple library crates? That’s where cargo workspace comes for the rescue.
- Workspaces help you manage multiple related packages that are being developed
- Packages in a workspace share common dependency resolution, since they have only one
Cargo.lock
file. - Packages in a workspace also share one output directory and release profiles.
Creating a Workspace
We are going to create one binary that depends on one library.
- First library having
add_one
function.
Begin by creating a directory and opening it in an editor:
mkdir add
cd add
code .
Next we’ll add a Cargo.toml
file to configure our workspace, by creating a worspace
section instead of a package
section.
- Then we’ll specify the members of the workspace called workspace members specifying the path to the packages.
[workspace]
members = [
"adder"
]
Then we’ll add a new package adder
and even try building it:
cargo new adder
cargo build
The newly created package will not have a target folder or Cargo.lock
file but instead at root of our workspace, signifying the packages in a workspace are meant to depend on each other. Meaning if each package had its own target directory, when you would compile that package, you would have to also compile all its dependencies. But now they are combinely managed in a single workspace with same dependencies, reducing the amount of compilation required.
Creating Second pacakge in Workspace
Update the root Cargo.toml
file:
[workspace]
members = [
"adder",
"add-one",
]
We’ll add a second package add-one
and specifying --lib
for library:
cargo new add-one --lib
Update the add-one/src/lib.rs
file:
pub fn add_one(x: i32) -> i32 {
x +1
}
Next, we need to specify that our adder
binary depends on add-one
library. We’ll do this by updating adder
’s Cargo.toml
file:
- Cargo by default don’t assume that crates within a workspace depend on each other.
[dependencies]
add-one = { path = "../add-one" }
Now we can use our newly created library in adder
binary, in adder/src/main.rs
:
use add_one;
fn mian() {
let num = 10;
println!(
"Hello, world! {} plus one is {}!",
num,
add_one::add_one(num)
);
}
To build our workspace run the build command from the root of workspace:
cargo build
Next we can the adder binary from the root of our workspace by running:
cargo run --package adder
External dependencies
Since all the packages uses one single Cargo.lock
for dependency resolution, this ensures that the packages are compatible with each other.
- If we add a dependency to
add-one
package andadder
package, they both will resolve to the same version.
If we add a rand
dependency to Cargo.toml
file of add-one
package and use it somewhere in lib.rs
:
[dependencies]
rand = "0.8.3"
use rand;
pub fn add_one(x: i32 ) -> i32 {
// ...
Then from the root of the package we can build the workspace: cargo build
which adds rand
as a dependency for the add-one
package.
We can’t use the rand
dependency however in adder
package until we add it as a dependency for adder
in it’s Cargo.toml
file:
[dependencies]
add-one = { path = "../add-one" }
rand = "0.8.3"
Adding a Test to a Workspace
Let’s add one test module inside lib.rs
file of add-one
package:
pub fn add_one(x: i32) -> i32 {
//...
// ..
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(3, add_one(2));
}
}
Now run cargo test
from workspace root. This will run all package-tests and documentation tests.
If we wanted to run tests for a specific package we could run it using -p
or --package
option specfying the specific package:
cargo test -p add-one
To publish a package from a workspace we have to do it individually for each package.
Installing Binaries from Crates.io
Although this isn’t meant as a replacement for package managers like dnf
or apt
but as a convenient tool to install tools published on crates.io having a binary target.
- All binaries are stored in Rust installation routes bin directory.
If installed via rustup
this would be the path to that bin directory (can be requirement to add it to PATH, so that other programs use the Rust installed binaries):
~/.cargo/bin
Let try this out by installing riprep
, the implementation of grep
in Rust.
cargo install ripgrep
Extending Cargo
If you have a binary named starting with cargo-
, say cargo-something
, then this can be used by cargo
as a command (sub command) to extend its functionality:
cargo something