Skip to content

Cargo 教程

概述

Cargo 是 Rust 的包管理器和构建系统,它处理依赖管理、项目构建、测试运行、文档生成等任务。本章将深入学习 Cargo 的各种功能和最佳实践。

🚀 Cargo 基础

什么是 Cargo

Cargo 是 Rust 生态系统的核心工具,提供:

  • 项目创建和管理
  • 依赖管理和版本控制
  • 代码编译和构建
  • 测试执行和基准测试
  • 文档生成和发布

Cargo 项目结构

rust
// 典型的 Cargo 项目结构
my_project/
├── Cargo.toml          // 项目配置文件
├── Cargo.lock          // 依赖锁定文件
├── src/                // 源代码目录
│   ├── main.rs        // 二进制项目入口
│   ├── lib.rs         // 库项目入口
│   └── bin/           // 额外的二进制文件
├── examples/          // 示例代码
├── tests/             // 集成测试
├── benches/           // 性能基准测试
├── build.rs           // 构建脚本
└── target/            // 构建输出目录

📦 项目创建和管理

创建新项目

bash
# 创建二进制项目
cargo new my_binary_project
cd my_binary_project

# 创建库项目
cargo new my_library --lib

# 在现有目录中初始化项目
mkdir existing_project
cd existing_project
cargo init

# 创建项目时指定版本控制系统
cargo new my_project --vcs git
cargo new my_project --vcs none

Cargo.toml 配置详解

toml
# 项目基本信息
[package]
name = "my_awesome_project"           # 项目名称
version = "0.1.0"                     # 版本号
edition = "2021"                      # Rust 版本
authors = ["Your Name <your.email@example.com>"]
license = "MIT OR Apache-2.0"        # 开源许可证
description = "一个很棒的 Rust 项目"    # 项目描述
homepage = "https://example.com"      # 项目主页
repository = "https://github.com/username/project"
readme = "README.md"                  # README 文件
keywords = ["cli", "tool", "utility"] # 关键词
categories = ["command-line-utilities"] # 分类

# 依赖配置
[dependencies]
serde = "1.0"                         # 简单版本
tokio = { version = "1.0", features = ["full"] } # 带特性
reqwest = { version = "0.11", default-features = false, features = ["json"] }
log = "0.4"

# 开发依赖(仅在开发时使用)
[dev-dependencies]
assert_cmd = "2.0"
tempfile = "3.0"
criterion = "0.5"

# 构建依赖
[build-dependencies]
cc = "1.0"

# 目标特定依赖
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

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

# 可选依赖
[dependencies]
serde_json = { version = "1.0", optional = true }

[features]
default = ["json"]                    # 默认特性
json = ["serde_json"]                # 自定义特性

# 二进制目标
[[bin]]
name = "my_app"
path = "src/main.rs"

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

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

# 基准测试
[[bench]]
name = "my_benchmark"
harness = false

# 项目元数据
[profile.release]
opt-level = 3                         # 优化级别
debug = false                         # 调试信息
strip = true                          # 删除符号
lto = true                           # 链接时优化
codegen-units = 1                    # 代码生成单元

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

🔧 依赖管理

添加依赖

bash
# 添加依赖
cargo add serde
cargo add tokio --features full
cargo add reqwest --no-default-features --features json

# 添加开发依赖
cargo add --dev assert_cmd

# 添加构建依赖
cargo add --build cc

# 指定版本
cargo add serde@1.0.150

版本规范

toml
[dependencies]
# 精确版本
serde = "=1.0.150"

# 兼容版本(默认)
serde = "1.0"          # >=1.0.0, <2.0.0
serde = "1.0.150"      # >=1.0.150, <2.0.0

# 语义化版本
serde = "~1.0.150"     # >=1.0.150, <1.1.0
serde = "^1.0.150"     # >=1.0.150, <2.0.0

# 版本范围
serde = ">=1.0, <2.0"

# Git 依赖
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" }

# 本地路径依赖
my_lib = { path = "../my_library" }

# 条件依赖
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

特性管理

rust
// lib.rs - 定义特性
#[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 处理功能
}

// 使用特性
fn main() {
    #[cfg(feature = "json")]
    {
        let data = r#"{"name": "Rust", "type": "Language"}"#;
        match json_support::parse_json(data) {
            Ok(value) => println!("解析成功: {:?}", value),
            Err(e) => println!("解析失败: {}", e),
        }
    }
}
bash
# 构建时启用特定特性
cargo build --features json
cargo build --features "json,xml"
cargo build --no-default-features --features json
cargo build --all-features

🏗️ 构建和编译

基本构建命令

bash
# 检查代码(快速语法检查)
cargo check

# 构建项目
cargo build

# 发布构建(优化)
cargo build --release

# 构建特定目标
cargo build --bin my_app
cargo build --example demo
cargo build --lib

# 交叉编译
cargo build --target x86_64-pc-windows-gnu
cargo build --target wasm32-unknown-unknown

构建配置

toml
# Cargo.toml 中的构建配置
[profile.dev]
opt-level = 0      # 无优化
debug = true       # 包含调试信息
overflow-checks = true  # 整数溢出检查

[profile.release]
opt-level = 3      # 最高级优化
debug = false      # 无调试信息
lto = true         # 链接时优化
panic = "abort"    # panic 时直接终止

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

# 自定义配置
[profile.production]
inherits = "release"
opt-level = 3
debug = false
strip = true
lto = "fat"

条件编译

rust
// 基于操作系统的条件编译
#[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()
}

// 基于特性的条件编译
#[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>> {
    // 同步实现
    Ok("同步数据".to_string())
}

// 调试版本专用代码
#[cfg(debug_assertions)]
fn debug_only_function() {
    println!("这只在调试版本中执行");
}

// 发布版本专用代码
#[cfg(not(debug_assertions))]
fn release_only_function() {
    // 发布版本的优化代码
}

🧪 测试和基准

单元测试

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("不能除以零".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!("这个测试应该 panic");
    }

    #[test]
    #[ignore]
    fn expensive_test() {
        // 耗时测试,默认不运行
    }
}

集成测试

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());
}

文档测试

rust
/// 计算两个数的和
/// 
/// # 示例
/// 
/// ```
/// use my_project::add;
/// 
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
/// 
/// # 注意
/// 
/// 这个函数可能会整数溢出
/// 
/// ```should_panic
/// use my_project::add;
/// 
/// // 这会导致溢出(在某些平台上)
/// let result = add(i32::MAX, 1);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

性能基准测试

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);

测试命令

bash
# 运行所有测试
cargo test

# 运行特定测试
cargo test test_add
cargo test integration

# 显示测试输出
cargo test -- --nocapture

# 运行忽略的测试
cargo test -- --ignored

# 并行测试控制
cargo test -- --test-threads=1

# 运行文档测试
cargo test --doc

# 运行基准测试
cargo bench

# 代码覆盖率(需要安装 cargo-tarpaulin)
cargo install cargo-tarpaulin
cargo tarpaulin --out Html

📚 文档生成

生成文档

bash
# 生成并打开文档
cargo doc --open

# 生成包含私有项的文档
cargo doc --document-private-items

# 生成依赖项文档
cargo doc --no-deps

# 只检查文档示例
cargo test --doc

文档注释

rust
//! # My Crate
//! 
//! 这是我的 crate 的顶级文档。
//! 
//! ## 功能
//! 
//! - 数学运算
//! - 错误处理
//! - 异步支持

/// 表示一个计算器
/// 
/// # 示例
/// 
/// ```
/// use my_project::Calculator;
/// 
/// let calc = Calculator::new();
/// let result = calc.add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub struct Calculator {
    /// 当前值
    pub value: i32,
}

impl Calculator {
    /// 创建新的计算器
    /// 
    /// # 示例
    /// 
    /// ```
    /// # use my_project::Calculator;
    /// let calc = Calculator::new();
    /// assert_eq!(calc.value, 0);
    /// ```
    pub fn new() -> Self {
        Self { value: 0 }
    }
    
    /// 执行加法运算
    /// 
    /// # 参数
    /// 
    /// * `a` - 第一个数
    /// * `b` - 第二个数
    /// 
    /// # 返回值
    /// 
    /// 返回两数之和
    /// 
    /// # 示例
    /// 
    /// ```
    /// # 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
    }
}

📦 发布和分发

准备发布

bash
# 检查包是否可以发布
cargo publish --dry-run

# 打包项目
cargo package

# 查看包内容
cargo package --list

发布到 crates.io

bash
# 登录 crates.io(需要 API token)
cargo login

# 发布包
cargo publish

# 撤回版本(72小时内)
cargo yank --vers 1.0.1

# 取消撤回
cargo yank --vers 1.0.1 --undo

本地安装

bash
# 从 crates.io 安装
cargo install my_tool

# 从本地路径安装
cargo install --path .

# 从 Git 仓库安装
cargo install --git https://github.com/user/repo

# 卸载
cargo uninstall my_tool

🛠️ 高级功能

工作空间(Workspace)

toml
# 根目录 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 }

构建脚本

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

fn main() {
    // 设置环境变量
    println!("cargo:rustc-env=BUILD_TIME={}", chrono::Utc::now());
    
    // 链接外部库
    println!("cargo:rustc-link-lib=ssl");
    println!("cargo:rustc-link-search=native=/usr/local/lib");
    
    // 重新运行条件
    println!("cargo:rerun-if-changed=src/proto/");
    
    // 生成代码
    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();
}

自定义命令

bash
# 安装有用的 Cargo 扩展
cargo install cargo-edit          # cargo add, cargo rm
cargo install cargo-watch         # cargo watch
cargo install cargo-expand        # 宏展开
cargo install cargo-audit         # 安全审计
cargo install cargo-outdated      # 检查过时依赖
cargo install cargo-tree          # 依赖树
cargo install cargo-deny          # 依赖检查

📝 本章小结

通过本章学习,你应该掌握了:

Cargo 核心功能

  • ✅ 项目创建和结构管理
  • ✅ 依赖管理和版本控制
  • ✅ 构建配置和优化
  • ✅ 测试和基准测试

高级特性

  • ✅ 特性标志和条件编译
  • ✅ 工作空间管理
  • ✅ 文档生成和发布
  • ✅ 构建脚本和自定义命令

最佳实践

  1. 使用语义化版本控制
  2. 合理组织项目结构
  3. 编写全面的测试和文档
  4. 利用特性标志管理功能

继续学习下一章 - Rust 快速上手

本站内容仅供学习和研究使用。