HOW-TO: Use Cargo for Package Management in Rust
Master Cargo, Rust's package manager and build system. Learn dependency management, creating projects, publishing crates, and building production-ready packages.
HOW-TO: Use Cargo for Package Management in Rust
Overview
Cargo is Rust's official package manager and build system. It handles project scaffolding, dependency management, building, testing, and publishing. Think of it like npm for Node.js or pip for Pythonβbut more integrated into the language itself.
What you'll learn:
- Creating and managing Cargo projects
- Adding and updating dependencies
- Building, testing, and running projects
- Publishing crates to crates.io
- Working with workspaces
- Optimization and release builds
Prerequisites: Rust installed via rustup (see Howto Install Rust Linux)
Part 1: Creating Your First Project
Generate a New Project
cargo new my_project
cd my_project
What Cargo creates:
my_project/
βββ Cargo.toml # Project manifest (metadata, dependencies)
βββ Cargo.lock # Lock file (dependency versions, auto-generated)
βββ src/
βββ main.rs # Entry point (fn main {})
Project Types
Binary (executable):
cargo new my_app # Default: creates a binary project
cargo new --bin my_app # Explicit
Library:
cargo new --lib my_library # Creates lib.rs instead of main.rs
Directory Structure (Library)
my_library/
βββ Cargo.toml
βββ src/
β βββ lib.rs # Public API
βββ tests/
β βββ integration_test.rs
βββ benches/
βββ my_benchmark.rs
Part 2: Understanding Cargo.toml
Anatomy of Cargo.toml
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
license = "MIT"
description = "A brief description of your project"
repository = "https://github.com/you/my_project"
homepage = "https://github.com/you/my_project"
documentation = "https://docs.rs/my_project"
[dependencies]
serde = "1.0"
tokio = { version = "1.35", features = ["full"] }
log = "0.4"
[dev-dependencies]
criterion = "0.5"
[profile.release]
opt-level = 3
lto = true
Key Sections
| Section | Purpose |
|---|---|
[package] | Project metadata (name, version, author) |
[dependencies] | Runtime dependencies (included in binary) |
[dev-dependencies] | Test/benchmark dependencies (excluded from release) |
[profile.*] | Build optimization settings |
Edition
edition = "2021" # Latest (2021, 2018, 2015 also valid)
The edition controls language features and defaults. Use 2021 for new projects.
Part 3: Managing Dependencies
Adding Dependencies
Interactive (recommended for discovery):
cargo add serde
cargo add --dev criterion
Manual edit to Cargo.toml:
[dependencies]
serde = "1.0"
Then run:
cargo build # Downloads and compiles dependencies
Dependency Versions
Caret ranges (default, allows minor updates):
serde = "1.0" # Same as ^1.0 β allows 1.0.z, 1.y.z (not 2.0+)
serde = "1.0.5" # ^1.0.5 β allows 1.0.5, 1.0.6, 1.1.0, etc.
Tilde ranges (patch updates only):
serde = "~1.0" # Allows 1.0.z only (not 1.1+)
serde = "~1.0.5" # Allows 1.0.5 and 1.0.6, not 1.0.7
Exact versions:
serde = "=1.0.5" # Exactly 1.0.5
Wildcard:
serde = "1.*" # Any 1.x.y
Comparison operators:
serde = ">1.0" # Greater than 1.0
serde = ">=1.0, <2.0" # Range
Features
Enable optional functionality:
[dependencies]
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive", "json"] }
Check available features:
# On crates.io documentation or:
cargo tree --features "feature1,feature2"
Local Dependencies
[dependencies]
my_lib = { path = "../my_lib" }
Git Dependencies
[dependencies]
my_lib = { git = "https://github.com/user/my_lib.git", branch = "main" }
Removing Dependencies
cargo remove serde
Part 4: Building and Running
Development Build
cargo build # Unoptimized, fast compile
Creates: target/debug/my_project (or library artifact in target/debug/)
Release Build
cargo build --release # Optimized, slow compile, fast runtime
Creates: target/release/my_project
Running
cargo run # Build + run (debug)
cargo run --release # Build + run (release)
cargo run -- arg1 arg2 # Pass args to the program
Checking (Fast Verification)
cargo check # Verify code compiles without generating binary
# Much faster than `cargo build`, useful during development
Cleaning
cargo clean # Remove target/ directory
Part 5: Testing
Writing Tests
Unit tests (in source files):
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds_correctly() {
assert_eq!(add(2, 2), 4);
}
#[test]
fn it_handles_negatives() {
assert_eq!(add(-1, 1), 0);
}
}
Integration tests (tests/my_test.rs):
// tests/integration_test.rs
use my_library::add;
#[test]
fn integration_test() {
assert_eq!(add(10, 20), 30);
}
Running Tests
cargo test # Run all tests
cargo test --lib # Unit tests only
cargo test --test "*" # Integration tests only
cargo test test_name # Run specific test
cargo test -- --ignored # Run ignored tests only
cargo test -- --nocapture # Show println! output
cargo test --release # Test in release mode (slower compile, faster run)
Test Output
running 2 tests
test tests::it_adds_correctly ... ok
test tests::it_handles_negatives ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Part 6: Documentation
Writing Doc Comments
/// Adds two numbers together.
///
/// # Arguments
/// * `a` - First number
/// * `b` - Second number
///
/// # Returns
/// The sum of a and b
///
/// # Example
/// ```
/// use my_lib::add;
/// assert_eq!(add(2, 2), 4);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Generating Documentation
cargo doc # Generate HTML docs
cargo doc --open # Generate and open in browser
Creates: target/doc/my_project/index.html
Doc Tests
Doc examples are executable tests:
cargo test --doc # Run doc examples as tests
Part 7: Publishing to crates.io
Prerequisites
- Create account at https://crates.io
- Generate API token
- Authenticate locally:
cargo login
# Paste your token when prompted
Prepare Your Crate
Update Cargo.toml:
[package]
name = "my_awesome_crate"
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
license = "MIT" # or "Apache-2.0", "GPL-3.0", etc.
description = "A brief, searchable description"
repository = "https://github.com/user/my_awesome_crate"
documentation = "https://docs.rs/my_awesome_crate"
keywords = ["awesome", "crate", "example"] # Up to 5
categories = ["algorithms", "data-structures"] # Up to 5
readme = "README.md"
Verify Before Publishing
cargo publish --dry-run # Checks without uploading
Publish
cargo publish # Upload to crates.io
Version management:
# Update version in Cargo.toml
# Semantic versioning: MAJOR.MINOR.PATCH
# 0.1.0 β 0.2.0 (minor bump, compatible)
# 0.1.0 β 1.0.0 (major bump, breaking changes)
cargo publish
Yanking (Marking as Broken)
cargo yank --vers 0.1.0 # Hide from new installations
cargo yank --vers 0.1.0 --undo # Restore
Part 8: Workspaces
Creating a Workspace
my_workspace/
βββ Cargo.toml # Workspace root
βββ crates/
β βββ core/
β β βββ Cargo.toml
β βββ utils/
β βββ Cargo.toml
Root Cargo.toml:
[workspace]
members = ["crates/core", "crates/utils"]
resolver = "2"
Building Workspaces
cargo build # Build all members
cargo build -p core # Build specific member
cargo build --workspace # Explicit
cargo test --workspace # Test all
Part 9: Build Profiles
Default Profiles
[profile.dev]
opt-level = 0 # No optimization
debug = true # Include debug symbols
split-debuginfo = "off"
strip = false
[profile.release]
opt-level = 3 # Full optimization
debug = false
lto = true # Link-Time Optimization
codegen-units = 1 # Slower compile, better optimization
strip = true # Remove debug symbols
[profile.bench] # For benchmarking
inherits = "release"
[profile.test] # For testing
inherits = "dev"
Custom Profiles
[profile.custom]
inherits = "release"
opt-level = 2 # Balanced optimization
debug = true # Keep symbols for profiling
Build with custom profile:
cargo build --profile custom
Part 10: Troubleshooting
Common Issues
"cannot find crate for serde"
cargo build # Run build to fetch dependencies
# OR manually add: cargo add serde
Dependency conflict
cargo update # Updates to compatible versions
cargo update -p serde # Update specific crate
Slow builds
# Use faster incremental builds
cargo build # Not --release
# Parallel compilation
cargo build -j 4 # Use 4 threads
# Check what's slow
cargo build --timings # See which crates take longest
Clean rebuild
cargo clean
cargo build
Lock file conflicts (git/CI)
# Commit Cargo.lock for binaries
# Don't commit Cargo.lock for libraries (users can update)
git add Cargo.lock # For applications
# For libraries: add to .gitignore
Useful Commands Reference
| Command | Purpose |
|---|---|
cargo new | Create new project |
cargo init | Create project in current dir |
cargo build | Compile (debug) |
cargo build --release | Compile (optimized) |
cargo run | Build and run |
cargo check | Fast syntax check |
cargo test | Run tests |
cargo doc --open | Generate and view docs |
cargo add <crate> | Add dependency |
cargo remove <crate> | Remove dependency |
cargo update | Update dependencies |
cargo publish | Upload to crates.io |
cargo clean | Remove build artifacts |
cargo tree | Visualize dependencies |
cargo clippy | Lint suggestions |
cargo fmt | Format code |
Best Practices
β Do:
- Commit
Cargo.lockfor binary projects - Use meaningful crate names (lowercase, no underscores except between words)
- Version your crates with semantic versioning
- Add documentation and examples
- Keep dependencies up to date (
cargo update) - Use
cargo checkduring development - Split into libraries when possible (easier to test and reuse)
β Don't:
- Force exact versions unless absolutely necessary
- Publish incomplete or untested code
- Ignore compiler warnings
- Leave stale dependencies unaudited
- Disable important checks for speed
Next Steps
- Create a project:
cargo new my_app - Add dependencies:
cargo add serde tokio - Write code and tests
- Run tests:
cargo test - Generate docs:
cargo doc --open - Publish:
cargo publish(when ready)
Resources
- Official Cargo Book: https://doc.rust-lang.org/cargo/
- crates.io: https://crates.io
- Dependency search: https://docs.rs/
- Cargo commands: https://doc.rust-lang.org/cargo/commands/
- Related: Howto Install Rust Linux, Howto Rust Getting Started