Skip to content

Cargo Tutorial

Overview

Cargo is Rust's package manager and build system, handling dependency management, project building, test execution, documentation generation, and other tasks. This chapter will dive deep into Cargo's various features and best practices.

🚀 Cargo Basics

What is Cargo

Cargo is the core tool of the Rust ecosystem, providing:

  • Project creation and management
  • Dependency management and version control
  • Code compilation and building
  • Test execution and benchmarking
  • Documentation generation and publishing

Cargo Project Structure

rust
// Typical Cargo project structure
my_project/
├── Cargo.toml          // Project configuration file
├── Cargo.lock          // Dependency lock file
├── src/                // Source code directory
│   ├── main.rs        // Binary project entry point
│   ├── lib.rs         // Library project entry point
│   └── bin/           // Additional binary files
├── examples/          // Example code
├── tests/             // Integration tests
├── benches/           // Performance benchmarks
├── build.rs           // Build script
└── target/            // Build output directory

📦 Project Creation and Management

Creating New Projects

bash
# Create binary project
cargo new my_binary_project
cd my_binary_project

# Create library project
cargo new my_library --lib

# Initialize project in existing directory
mkdir existing_project
cd existing_project
cargo init

# Create project with version control system
cargo new my_project --vcs git
cargo new my_project --vcs none

Cargo.toml Configuration Details

toml
# Project basic information
[package]
name = "my_awesome_project"           # Project name
version = "0.1.0"                     # Version number
edition = "2021"                      # Rust edition
authors = ["Your Name <your.email@example.com>"]
license = "MIT OR Apache-2.0"        # Open source license
description = "An awesome Rust project"  # Project description
homepage = "https://example.com"      # Project homepage
repository = "https://github.com/username/project"
readme = "README.md"                  # README file
keywords = ["cli", "tool", "utility"] # Keywords
categories = ["command-line-utilities"] # Categories

# Dependencies configuration
[dependencies]
serde = "1.0"                         # Simple version
tokio = { version = "1.0", features = ["full"] } # With features
reqwest = { version = "0.11", default-features = false, features = ["json"] }
log = "0.4"

# Development dependencies (only used during development)
[dev-dependencies]
assert_cmd = "2.0"
tempfile = "3.0"
criterion = "0.5"

# Build dependencies
[build-dependencies]
cc = "1.0"

# Target-specific dependencies
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

[target.'cfg(unix)'.dependencies]
nix = "0.26"

# Optional dependencies
[dependencies]
serde_json = { version = "1.0", optional = true }

[features]
default = ["json"]                    # Default features
json = ["serde_json"]                # Custom features

# Binary targets
[[bin]]
name = "my_app"
path = "src/main.rs"

[[bin]]
name = "helper_tool"
path = "src/bin/helper.rs"

# Examples
[[example]]
name = "demo"
path = "examples/demo.rs"

# Benchmarks
[[bench]]
name = "my_benchmark"
harness = false

# Project metadata
[profile.release]
opt-level = 3                         # Optimization level
debug = false                         # Debug info
strip = true                          # Strip symbols
lto = true                           # Link-time optimization
codegen-units = 1                    # Code generation units

[profile.dev]
opt-level = 0
debug = true

🔧 Dependency Management

Adding Dependencies

bash
# Add dependencies
cargo add serde
cargo add tokio --features full
cargo add reqwest --no-default-features --features json

# Add development dependencies
cargo add --dev assert_cmd

# Add build dependencies
cargo add --build cc

# Specify version
cargo add serde@1.0.150

Version Specifications

toml
[dependencies]
# Exact version
serde = "=1.0.150"

# Compatible version (default)
serde = "1.0"          # >=1.0.0, <2.0.0
serde = "1.0.150"      # >=1.0.150, <2.0.0

# Semantic versioning
serde = "~1.0.150"     # >=1.0.150, <1.1.0
serde = "^1.0.150"     # >=1.0.150, <2.0.0

# Version range
serde = ">=1.0, <2.0"

# Git dependencies
tokio = { git = "https://github.com/tokio-rs/tokio.git" }
tokio = { git = "https://github.com/tokio-rs/tokio.git", branch = "main" }
tokio = { git = "https://github.com/tokio-rs/tokio.git", tag = "v1.0.0" }
tokio = { git = "https://github.com/tokio-rs/tokio.git", rev = "abc123" }

# Local path dependencies
my_lib = { path = "../my_library" }

# Conditional dependencies
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

Feature Management

rust
// lib.rs - Define features
#[cfg(feature = "json")]
pub mod json_support {
    use serde_json;

    pub fn parse_json(input: &str) -> serde_json::Result<serde_json::Value> {
        serde_json::from_str(input)
    }
}

#[cfg(feature = "xml")]
pub mod xml_support {
    // XML processing functionality
}

// Use features
fn main() {
    #[cfg(feature = "json")]
    {
        let data = r#"{"name": "Rust", "type": "Language"}"#;
        match json_support::parse_json(data) {
            Ok(value) => println!("Parse successful: {:?}", value),
            Err(e) => println!("Parse failed: {}", e),
        }
    }
}
bash
# Build with specific features enabled
cargo build --features json
cargo build --features "json,xml"
cargo build --no-default-features --features json
cargo build --all-features

🏗️ Building and Compilation

Basic Build Commands

bash
# Check code (quick syntax check)
cargo check

# Build project
cargo build

# Release build (optimized)
cargo build --release

# Build specific target
cargo build --bin my_app
cargo build --example demo
cargo build --lib

# Cross-compilation
cargo build --target x86_64-pc-windows-gnu
cargo build --target wasm32-unknown-unknown

Build Configuration

toml
# Build configuration in Cargo.toml
[profile.dev]
opt-level = 0      # No optimization
debug = true       # Include debug info
overflow-checks = true  # Integer overflow checks

[profile.release]
opt-level = 3      # Highest optimization
debug = false      # No debug info
lto = true         # Link-time optimization
panic = "abort"    # Abort on panic

[profile.test]
opt-level = 0
debug = true

# Custom profile
[profile.production]
inherits = "release"
opt-level = 3
debug = false
strip = true
lto = "fat"

Conditional Compilation

rust
// Conditional compilation based on OS
#[cfg(target_os = "windows")]
fn get_config_dir() -> String {
    "C:\\ProgramData\\MyApp".to_string()
}

#[cfg(target_os = "linux")]
fn get_config_dir() -> String {
    "/etc/myapp".to_string()
}

#[cfg(target_os = "macos")]
fn get_config_dir() -> String {
    "/Library/Application Support/MyApp".to_string()
}

// Conditional compilation based on features
#[cfg(feature = "async")]
async fn async_function() -> Result<String, Box<dyn std::error::Error>> {
    let response = reqwest::get("https://api.example.com/data").await?;
    let text = response.text().await?;
    Ok(text)
}

#[cfg(not(feature = "async"))]
fn sync_function() -> Result<String, Box<dyn std::error::Error>> {
    // Synchronous implementation
    Ok("Synchronous data".to_string())
}

// Debug-only code
#[cfg(debug_assertions)]
fn debug_only_function() {
    println!("This only runs in debug builds");
}

// Release-only code
#[cfg(not(debug_assertions))]
fn release_only_function() {
    // Release version optimized code
}

🧪 Testing and Benchmarks

Unit Tests

rust
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

pub fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("Cannot divide by zero".to_string())
    } else {
        Ok(a / b)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
        assert_eq!(add(-1, 1), 0);
    }

    #[test]
    fn test_divide_success() {
        assert_eq!(divide(10.0, 2.0).unwrap(), 5.0);
    }

    #[test]
    fn test_divide_by_zero() {
        assert!(divide(10.0, 0.0).is_err());
    }

    #[test]
    #[should_panic]
    fn test_panic() {
        panic!("This test should panic");
    }

    #[test]
    #[ignore]
    fn expensive_test() {
        // Expensive test, not run by default
    }
}

Integration Tests

rust
// tests/integration_test.rs
use my_project;

#[test]
fn test_public_api() {
    let result = my_project::add(2, 3);
    assert_eq!(result, 5);
}

#[test]
fn test_error_handling() {
    let result = my_project::divide(10.0, 0.0);
    assert!(result.is_err());
}

Documentation Tests

rust
/// Calculate the sum of two numbers
///
/// # Examples
///
/// ```
/// use my_project::add;
///
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
///
/// # Notes
///
/// This function may overflow on integer overflow
///
/// ```should_panic
/// use my_project::add;
///
/// // This will cause overflow (on some platforms)
/// let result = add(i32::MAX, 1);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

Performance Benchmarks

rust
// benches/benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use my_project::*;

fn bench_add(c: &mut Criterion) {
    c.bench_function("add", |b| {
        b.iter(|| add(black_box(100), black_box(200)))
    });
}

fn bench_divide(c: &mut Criterion) {
    c.bench_function("divide", |b| {
        b.iter(|| divide(black_box(100.0), black_box(3.0)))
    });
}

criterion_group!(benches, bench_add, bench_divide);
criterion_main!(benches);

Test Commands

bash
# Run all tests
cargo test

# Run specific tests
cargo test test_add
cargo test integration

# Show test output
cargo test -- --nocapture

# Run ignored tests
cargo test -- --ignored

# Parallel test control
cargo test -- --test-threads=1

# Run documentation tests
cargo test --doc

# Run benchmarks
cargo bench

# Code coverage (requires cargo-tarpaulin)
cargo install cargo-tarpaulin
cargo tarpaulin --out Html

📚 Documentation Generation

Generating Documentation

bash
# Generate and open documentation
cargo doc --open

# Generate documentation with private items
cargo doc --document-private-items

# Generate dependency documentation
cargo doc --no-deps

# Only check documentation examples
cargo test --doc

Documentation Comments

rust
//! # My Crate
//!
//! This is the top-level documentation for my crate.
//!
//! ## Features
//!
//! - Mathematical operations
//! - Error handling
//! - Async support

/// Represents a calculator
///
/// # Examples
///
/// ```
/// use my_project::Calculator;
///
/// let calc = Calculator::new();
/// let result = calc.add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub struct Calculator {
    /// Current value
    pub value: i32,
}

impl Calculator {
    /// Create a new calculator
    ///
    /// # Examples
    ///
    /// ```
    /// # use my_project::Calculator;
    /// let calc = Calculator::new();
    /// assert_eq!(calc.value, 0);
    /// ```
    pub fn new() -> Self {
        Self { value: 0 }
    }

    /// Perform addition
    ///
    /// # Arguments
    ///
    /// * `a` - First number
    /// * `b` - Second number
    ///
    /// # Returns
    ///
    /// Returns the sum of two numbers
    ///
    /// # Examples
    ///
    /// ```
    /// # use my_project::Calculator;
    /// let calc = Calculator::new();
    /// let result = calc.add(10, 20);
    /// assert_eq!(result, 30);
    /// ```
    pub fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }
}

📦 Publishing and Distribution

Preparing for Publishing

bash
# Check if package can be published
cargo publish --dry-run

# Package the project
cargo package

# View package contents
cargo package --list

Publishing to crates.io

bash
# Login to crates.io (requires API token)
cargo login

# Publish package
cargo publish

# Yank version (within 72 hours)
cargo yank --vers 1.0.1

# Unyank
cargo yank --vers 1.0.1 --undo

Local Installation

bash
# Install from crates.io
cargo install my_tool

# Install from local path
cargo install --path .

# Install from Git repository
cargo install --git https://github.com/user/repo

# Uninstall
cargo uninstall my_tool

🛠️ Advanced Features

Workspaces

toml
# Root Cargo.toml
[workspace]
members = [
    "app",
    "lib",
    "tools/*",
]

[workspace.dependencies]
serde = "1.0"
tokio = "1.0"

# app/Cargo.toml
[package]
name = "app"
version = "0.1.0"
edition = "2021"

[dependencies]
lib = { path = "../lib" }
serde = { workspace = true }

Build Scripts

rust
// build.rs
use std::env;
use std::path::PathBuf;

fn main() {
    // Set environment variable
    println!("cargo:rustc-env=BUILD_TIME={}", chrono::Utc::now());

    // Link external library
    println!("cargo:rustc-link-lib=ssl");
    println!("cargo:rustc-link-search=native=/usr/local/lib");

    // Rebuild conditions
    println!("cargo:rerun-if-changed=src/proto/");

    // Generate code
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = PathBuf::from(&out_dir).join("generated.rs");

    std::fs::write(
        &dest_path,
        "pub const GENERATED: &str = \"Hello from build script!\";",
    ).unwrap();
}

Custom Commands

bash
# Install useful Cargo extensions
cargo install cargo-edit          # cargo add, cargo rm
cargo install cargo-watch         # cargo watch
cargo install cargo-expand        # macro expansion
cargo install cargo-audit         # security audit
cargo install cargo-outdated      # check outdated dependencies
cargo install cargo-tree          # dependency tree
cargo install cargo-deny          # dependency checking

📝 Chapter Summary

By studying this chapter, you should have mastered:

Cargo Core Features

  • ✅ Project creation and structure management
  • ✅ Dependency management and version control
  • ✅ Build configuration and optimization
  • ✅ Testing and benchmarking

Advanced Features

  • ✅ Feature flags and conditional compilation
  • ✅ Workspace management
  • ✅ Documentation generation and publishing
  • ✅ Build scripts and custom commands

Best Practices

  1. Use semantic versioning
  2. Organize project structure properly
  3. Write comprehensive tests and documentation
  4. Use feature flags to manage functionality

Continue Learning: Next Chapter - Rust Quick Start

Content is for learning and research only.