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 noneCargo.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 核心功能
- ✅ 项目创建和结构管理
- ✅ 依赖管理和版本控制
- ✅ 构建配置和优化
- ✅ 测试和基准测试
高级特性
- ✅ 特性标志和条件编译
- ✅ 工作空间管理
- ✅ 文档生成和发布
- ✅ 构建脚本和自定义命令
最佳实践
- 使用语义化版本控制
- 合理组织项目结构
- 编写全面的测试和文档
- 利用特性标志管理功能
继续学习:下一章 - Rust 快速上手