Cargo is the official package and dependency management tool for the Rust programming language.
It has a great deal of built-in features for assisting you with the development process, and it also supports further extension through subcommands.
In this post, we will be exploring some of the great things you can do with cargo
itself, while also taking a look at useful cargo
subcommands to supercharge your Rust development.
Sounds good? Awesome, so let's dive in!
Here are some of the day-to-day commands that cargo
provides:
# build your project
cargo build
cargo b
cargo build --release # release mode
# check your code (emits compiler warnings & errors)
cargo check
cargo c
# run the project's tests
cargo test
cargo t
cargo test NAME # run tests with NAME in the test name
cargo test --lib # only test library code
cargo test --bin NAME # test a binary named NAME
cargo test -j 1 # use single-threaded testing
# build documentation for your crate + depedencies
cargo doc
cargo d
cargo doc --open # open the docs in a browser
# make a new Rust project
cargo init # new binary/application project
cargo init --lib # new library project
# build & immediately run code
cargo run
cargo r
cargo run --example NAME # run an example named NAME
# remove build artifacts (`target` directory)
cargo clean
Rust's crate ecosystem is another major benefit to using Rust.
Good news? cargo
supports working with crates right out of the box!
# add latest version of CRATE_NAME
cargo add CRATE_NAME
# example: cargo add serde
# add a specific version of a crate
cargo add CRATE_NAME@VERSION
# example: cargo add serde@1
# add CRATE_NAME with features `feat_1` and `feat_2` enabled
cargo add CRATE_NAME -F feat_1 feat_2
# example cargo add serde -F derive
# add a crate from a git repo
cargo add --git URI
# example cargo add https://github.com/serde-rs/serde
# remove dependency on CRATE_NAME
cargo remove CRATE_NAME
The built-in cargo
commands are sufficient to build and manage your Rust code. However, sometimes it helps to have a little extra to make things even better.
That's where cargo
subcommands come in.
There are multiple subcommands available for esoteric scenarios like writing Postgres plugins or securing Linux applications with eBPF, but there are also general-purpose subcommands useful for most projects.
Here's a short list of subcommands that you can utilize in your own Rust projects today, and some quick notes on how they work.
cargo-update
Subcommands get installed using the cargo install
command, but once installed there is no convenient way to update them.
Well, not any more! The cargo-update
subcommand solves this problem by providing a subcommand to update your subcommands.
You can install it with:
cargo install cargo-update
Once installed, you can update your subcommands with:
cargo install-update -a
Simple! Now you can get the most up-to-date subcommands for working with your Rust projects.
cargo-watch
cargo install cargo-watch
cargo-watch is a source monitoring tool that will run cargo check
(or any arbitrary shell command) whenever your source code changes.
Why care?
Well, this makes it easy to have a terminal open with the latest errors and warnings from the compiler.
cargo-nextest
cargo install --locked cargo-nextest
cargo-nextest is a test runner that provides an enhanced test result reporting interface, and runs up to 60% faster than the standard cargo test
.
Not only that, but it also provides:
Once installed, you can run your project's tests with nextest
by using
cargo nextest run
Sidenote:
cargo-nextest
is a drop-in replacement forcargo test
but with added features.
cargo-readme
cargo install cargo-readme
Every project needs a README.md
file, but writing them can be a pain.
cargo-readme
handles this by generating a README.md
file from the documentation comments in your source code.
To get a usable README.md
, just run:
cargo readme > README.md
Also? cargo-readme
uses templates to generate the README.md
file, so if you have a style that you prefer, or if you simply want to include additional information in your READMEs, you can create your own template with these additions.
cargo-make
cargo install cargo-make
cargo-make
is a command runner that provides two important things:
This enables the creation of workflows which cargo-make
can then execute.
For example
cargo-make
enables the combination of cargo test
, cargo format
, and cargo build
into a single command such as custom-build
.
This means that whenever we run cargo make custom-build
, all three commands will run, which can be incredibly helpful.
While Command dependencies allows creation of sequential workflows that will abort if any command in the sequence fails. An example of this could be making cargo test
depend on cargo check
producing no warnings, before it runs any further.
The best part of cargo-make
is the support for writing scripts to do whatever tasks the project requires.
Why? Well, since Rust is multi-platform, cargo-make
comes with support for duckscript
and Rust code for scripting. This then allows the scripts to run on any system that supports compiling Rust code!
cargo-make
has a comprehensive amount of options, so be sure to check out the usage section of the docs in order to get the most out of the tool.
cargo-expand
cargo install cargo-expand
When working with macros, it can sometimes be difficult to determine what code gets generated.
But with cargo-expand, you can get a printout of the code generated by macros, which makes macro development much easier.
One of Rust's biggest strengths is being able to produce correct code.
Here's a non-exhaustive list of the subcommands I've found to be the most useful.
cargo-tarpaulin
cargo install cargo-tarpaulin
cargo-tarpaulin
generates code coverage reports.
These reports allow you to discover how much of your code has test coverage and it identifies covered lines.
By default, tarpaulin
outputs the report in the terminal, but it supports different output formats.
For example
If you want to produce an HTML report, you can do so with:
cargo tarpaulin -o html
tarpaulin
will then output the report to tarpaulin-report.html
in the project root directory and you can view the report by opening it in your browser, or with the open
command (OSX/Linux): open tarpaulin-report.html
.
cargo-semver-checks
cargo install cargo-semver-checks
Rust crates use Semantic Versioning (SemVer) to communicate when breaking changes will occur, and Semantic versioning specifies versions using the triple MAJOR.MINOR.PATCH
.
MAJOR
identifies an API breaking changeMINOR
is a backwards compatible change in functionality, andPATCH
handles backwards compatible bugfixesThe cargo-semver-checks
subcommand will compare your code to a baseline and identify changes that would result in violating SemVer.
This means that if you make a change to your code and don't update the version number as indicated by SemVer, cargo-semver-checks
will list out the problems and identify which part of the SemVer triple to use for the listed issue.
For example
Adding or removing an enum
variant is a breaking API change because match
blocks are exhaustive.
For this reason, adding or removing a variant requires the MAJOR
version of the crate to increase. And so, if you don't update the MAJOR
version number, and then make the change to the enum
, cargo-semver-checks
will produce an error indicating that there is a change with enum
variants.
The error warning output will look something like this:
Description:
A publicly-visible enum without #[non_exhaustive] has a new variant.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new
impl: https://github.com/obi1kenobi/cargo-semver-check/tree/v0.15.2/src/queries/enum_variant_added.ron
Failed in:
variant Messages:Goodbye in src/main.rs:3
Final [ 0.037s] semver requires new major version: 1 major and 0 minor checks failed
Important: By default,
semver-checks
will use code published to the crates.io registry as a baseline for comparison.
However, you can use the --baseline-rev
flag to set the baseline to a git
revision, like so:
cargo semver-checks check-release --baseline-rev REVISION_HASH -v
This makes it easier to update versions on private crates, or for crates that aren't yet published to crates.io.
cargo-outdated
cargo install --locked cargo-outdated
cargo-outdated
is a straightforward subcommand for discovering outdated dependencies.
Running cargo outdated
will list out all of your dependencies that have gone out of date, along with the version you are using, and the latest version available.
You can use then this information to decide when you want to update the dependencies of your application.
cargo-audit
Security is a critical part of every software project, but it can be hard to stay on top of it, if it's not your main focus.
Good news? cargo-audit provides a way to audit the current security of your project, by checking all dependencies against known vulnerabilities published to the RustSec Advisory Database.
You simply run cargo audit
, and then it will check the database against your code. If any of your dependencies have matching versions, you'll get a report indicating what action to take in order to fix the vulnerability.
Safety first!
cargo-license
cargo install cargo-license
Maintaining proper licensing is also a critical part of software development.
Whether you are working on a commercial product or an open source project, you'll need to make sure you adhere to the code licenses.
cargo-license helps with this by enumarating all of your dependencies and producing a report with all the licenses used, and which crates use which licenses.
cargo-deny
cargo install --locked cargo-deny && cargo deny init
Most of the subcommands mentioned in this section have reported information that you can then evaluate yourself and use to make a decision, but cargo-deny
changes this by instead reporting errors when some criteria aren't met.
The criteria cargo-deny
supports are:
For example
If you want your project to use MIT licensed code, you can configure cargo-deny
to check all the licenses. Then, if any dependencies have a license other than MIT, it will produce an error.
Simple!
If you combine this with cargo-make and CI, you can even create automations that ensure your project meets the standards you specify.
cargo-geiger
cargo install cargo-geiger
unsafe
code blocks in Rust allow for operations that can introduce security risks that aren't otherwise possible in safe Rust.
Developers take extra effort to ensure that unsafe
blocks don't introduce security problems, but it is still a potential risk. For this reason, some developers may want to keep unsafe
code to a minimum in order to reduce the potential attack surface of an application.
cargo-geiger helps with this by generating a report which lists the total count of unsafe
code present in all dependencies.
Rust can run on different operating systems, and the packaging standard differs across these systems.
cargo
subcommands are here to make it easy to generate packages right from cargo
:
System / Environment | Subcommand |
---|---|
Arch Linux | cargo-aur |
Debian/Ubuntu | cargo-deb |
Fedora/CentOS/RHEL | cargo-generate-rpm |
Windows | cargo-wix |
Android | cargo-apk |
Gentoo | cargo-ebuild |
OSX | cargo-bundle |
WASM | cargo-web |
WASI | cargo-wasi |
As you can see, these Cargo
subcommands can be incredibly helpful to manage your own Rust projects and dependencies, as well as streamline your workflow.
If you're not entirely sure how to use these subcommands, or if you simply want to learn more about programming with Rust, then check out my complete Rust Programming course.
In it, you'll learn how to get to the level where you can code and build your own real-world applications using Rust so that you can get hired this year.
Start my Rust course for free here. (No signup or credit card required, just start learning!)
If you've made it this far, you're clearly interested in Rust so definitely check out all of my Rust posts and content: