Skip to main content

Command Palette

Search for a command to run...

Meet Cargo

Exploring Rust's Build and Package Manager

Updated
11 min read
Meet Cargo

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? 😉

Meet Rust

Part 2 of 4

A step-by-step journey into Rust, covering core concepts from basics to more advanced topics. We start with simple building blocks like functions, control flow and Cargo, and gradually move towards ownership, borrowing and real-world patterns.

Up next

Meet Rust Types and Variables

A quick overview of mutability, shadowing, constants and primitive types