Meet Cargo
Exploring Rust's Build and Package Manager

Hi folks!
We continue exploring the Rust ecosystem, and today we are talking about Cargo.
In the first article we already used Cargo to create a project and run it. In this article we will explore this tool a little bit deeper.
This article is just a small overview of this great tool.
I want to try to show what you can do with it. For more information, check the Cargo book.
Now, let’s go!
What is Cargo in Rust
Cargo is a package manager, but not in the classic understanding.
Cargo can help us with many things like generating a new project and running it, adding new dependencies to our project and building it, running tests and even generating documentation. And this is not a full list of possibilities.
First of all, let’s check the version of our Cargo tool. It also shows us that everything is installed and works as expected. Run in your terminal cargo version:
% cargo version
cargo 1.93.0 (083ac5135 2025-12-15)
Great, now we know that everything is fine and we can continue exploring Cargo.
Core Cargo Commands
Run cargo --list and now we can see all commands that Cargo provides.
% cargo --list
Installed Commands:
add Add dependencies to a Cargo.toml manifest file
b alias: build
bench Execute all benchmarks of a local package
build Compile a local package and all of its dependencies
c alias: check
check Check a local package and all of its dependencies for errors
clean Remove artifacts that cargo has generated in the past
clippy Checks a package to catch common mistakes and improve your Rust code.
config Inspect configuration values
d alias: doc
doc Build a package's documentation
fetch Fetch dependencies of a package from the network
fix Automatically fix lint warnings reported by rustc
fmt Formats all bin and lib files of the current crate using rustfmt.
generate-lockfile Generate the lockfile for a package
git-checkout REMOVED: This command has been removed
help Displays help for a cargo command
info Display information about a package
init Create a new cargo package in an existing directory
install Install a Rust binary
locate-project Print a JSON representation of a Cargo.toml file's location
login Log in to a registry.
logout Remove an API token from the registry locally
metadata Output the resolved dependencies of a package, the concrete used versions including overrides, in machine-readable format
miri
new Create a new cargo package at <path>
owner Manage the owners of a crate on the registry
package Assemble the local package into a distributable tarball
pkgid Print a fully qualified package specification
publish Upload a package to the registry
r alias: run
read-manifest DEPRECATED: Print a JSON representation of a Cargo.toml manifest.
remove Remove dependencies from a Cargo.toml manifest file
report Generate and display various kinds of reports
rm alias: remove
run Run a binary or example of the local package
rustc Compile a package, and pass extra options to the compiler
rustdoc Build a package's documentation, using specified custom flags.
search Search packages in the registry. Default registry is crates.io
t alias: test
test Execute all unit and integration tests and build examples of a local package
tree Display a tree visualization of a dependency graph
uninstall Remove a Rust binary
update Update dependencies as recorded in the local lock file
vendor Vendor all dependencies for a project locally
verify-project DEPRECATED: Check correctness of crate manifest.
version Show version information
yank Remove a pushed crate from the index
You can see that this is a very impressive list. Let’s check some of these commands.
Creating and Running a Rust Project with Cargo
Of course, the first thing you will do with Cargo is generate applications and libraries.
To generate a binary executable application you should run: cargo new my_app_name --bin.
In this case Cargo uses the binary template for your application. Also, for this type of application we can skip the --bin flag, because it’s used by default: cargo new my_app_name
% cargo new my_app --bin
Creating binary (application) `my_app` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Okay, but what about libraries? This type of project doesn’t have a main function, but we can export functions from this project and use them in other projects.
To generate a library project just run: argo new my_lib_name --lib
% cargo new my_lib --lib
Creating library `my_lib` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
cargo new generates a Rust project in a new folder with the name we provide in the command.
If we already have a folder and want to initialise a new Rust project inside it, we can use cargo init or cargo init --lib.
Okay, now we have a project, but how can we run it?
We can use cargo run. This command runs our project.
Understanding Cargo.toml (The Manifest File)
Cargo.toml is the file that helps us configure our application. It includes main information about the project, information about dependencies, and options for build profiles.
For newly generated apps it doesn’t look very impressive, but it can look like this:
% cat Cargo.toml
[package]
name = "my_lib"
version = "0.1.0"
edition = "2024"
description = "My cool library"
repository = "https://github.com/my_lib_repo"
homepage = "https://my_lib_page.com"
authors = ["John Doe <author@yahoo.com>"]
license = "MIT"
[dependencies]
rand = "0.8.5"
[profile.debugging]
inherits = "dev"
debug = true
This file will grow with your project.
Running Tests with Cargo
Cargo automatically finds tests and runs them. cargo test compiles the code and executes all functions marked with the #[test] attribute.
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
If we run: cargo test
% cargo test
Compiling tetlib v0.1.0 (/rust/tetlib)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.33s
Running unittests src/lib.rs (target/debug/deps/tetlib-c6985376b89cbb1b)
running 1 test
test tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests tetlib
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Cargo will compile the project and execute all unit and integration tests.
We can also filter tests by name. For example: cargo test works
This runs all tests that contain works in the function name.
Formatting and Linting with cargo fmt and Clippy
rustfmt
Cargo can help us here as well.
To use the formatting feature, we need to install rustfmt. It’s the official tool for formatting Rust code according to the official style guide.
Just run: rustup component add rustfmt. And now we can use cargo fmt
% cat src/lib.rs
pub fn add(left: u64, right: u64) -> u64 {left + right}
% cargo fmt
% cat src/lib.rs
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
Note: the cat command (like cat src/lib.rs) just shows the content of the file.
After running cargo fmt, the code will be formatted automatically according to Rust style conventions.
clippy
We can also use Clippy - it’s a linter for Rust code. This tool tries to find errors and non-optimal constructions.
Don’t forget to install it if needed: rustup component add clippy.
Then run:
% cargo clippy
warning: function `print` is never used
--> src/lib.rs:1:4
|
1 | fn print() -> () {
| ^^^^^
|
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
warning: unneeded unit return type
--> src/lib.rs:1:11
|
1 | fn print() -> () {
| ^^^^^^ help: remove the `-> ()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.93.0/index.html#unused_unit
= note: `#[warn(clippy::unused_unit)]` on by default
warning: `tetlib` (lib) generated 2 warnings (run `cargo clippy --fix --lib -p tetlib` to apply 1 suggestion)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Clippy does not just tell you that something is wrong, it also shows hints and provides links to further information if needed.
Using cargo check for Faster Builds
I also want to say a few words about cargo check. This command performs a fast compilation check without generating binaries. It is much faster than cargo build, especially for large projects.
In your work you can use scenarios like this:
On your local machine you can run cargo check
You can configure your IDE to run it on save
Before commit you can run cargo fmt
In a CI/CD pipeline you can use steps like:
cargo check -> cargo fmt -> cargo clippy -> cargo test
Managing Dependencies with cargo add and cargo rm
What about when we want to use functionality from other crates? Here Cargo helps us again.
With cargo add we can add new dependencies to our project:cargo add crate_name.
For example:
% cargo add rand
Updating crates.io index
Adding rand v0.10.0 to dependencies
Features:
+ alloc
+ std
+ std_rng
+ sys_rng
+ thread_rng
- chacha
- log
- serde
- simd_support
- unbiased
Updating crates.io index
Blocking waiting for file lock on package cache
Locking 44 packages to latest Rust 1.93.0 compatible versions
Adding anyhow v1.0.101
Adding bitflags v2.10.0
...
After this, our Cargo.toml will include:
[dependencies]
rand = "0.10.0"
We can also remove dependencies using:cargo rm name_of_deps
% cargo rm rand
Removing rand from dependencies
Visualizing Dependencies with cargo tree
cargo tree shows us the dependency tree of our project.
It can be useful for understanding which libraries are pulled in through other dependencies and for checking version conflicts in large projects.
% cargo tree
tetlib v0.1.0 (/rust/tetlib)
├── rand v0.10.0
│ ├── chacha20 v0.10.0
│ │ ├── cfg-if v1.0.4
│ │ └── rand_core v0.10.0
│ ├── getrandom v0.4.1
│ │ ├── cfg-if v1.0.4
│ │ ├── libc v0.2.181
│ │ └── rand_core v0.10.0
│ └── rand_core v0.10.0
└── serde v1.0.228
└── serde_core v1.0.228
Generating Documentation with cargo doc
Another great feature provided by Cargo is documentation generation.
Documentation is a very important part of any library or binary program, especially if you want to share your work with others.
Just add a simple comment to your function:
/// Simple function
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
And run: cargo doc
% cargo doc
Compiling libc v0.2.181
Compiling serde_core v1.0.228
Compiling serde v1.0.228
Documenting cfg-if v1.0.4
Documenting rand_core v0.10.0
Documenting chacha20 v0.10.0
Documenting libc v0.2.181
Documenting serde_core v1.0.228
Checking getrandom v0.4.1
Checking rand v0.10.0
Documenting getrandom v0.4.1
Documenting rand v0.10.0
Documenting serde v1.0.228
Documenting tetlib v0.1.0 (/rust/tetlib)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.38s
Generated /rust/tetlib/target/doc/tetlib/index.html
For large projects you can use:cargo doc --no-deps, for documentation without all external deps.
Then open target/doc/your_lib_name/index.html in your browser.
Cargo provides this feature out of the box. Just write documentation comments for your functions.
Understanding Build Profiles in Cargo
And finally, let’s say a few words about build profiles.
In Cargo.toml we can configure build profiles. We can customize them in various ways.
Example:
[profile.release]
debug = true
[profile.dev]
overflow-checks = false
In this example we enable debug symbols for the release build and disable overflow checks in the dev build.
Now we can use:cargo build —release
Or just: cargo build for the dev profile with the new settings.
You can find all possible profile options in the Cargo documentation.
Conclusion: Why Cargo Matters in the Rust Ecosystem
This is just a small overview of the great Rust tool known as Cargo. But even from this article, I hope you can see that Cargo provides many possibilities for working with Rust projects.
I just tried to show what you can do with it. Next, you can explore it by yourself in the Cargo book.
Thanks for reading. Please share your thoughts and comments.
Which Cargo command do you use most often? 😉


