第03章:Hello, Cargo
第03章:Hello, Cargo
3.1 为什么需要 Cargo
Cargo 是 Rust 的构建系统和包管理器,类似 Python 的 pip + setuptools、Node.js 的 npm、Go 的 go mod。它管理:
| 功能 | 说明 |
|---|
| 项目创建 | 生成标准项目结构 |
| 依赖管理 | 下载、编译、更新第三方库 |
| 构建编译 | 编译项目和依赖 |
| 测试运行 | 运行单元测试和集成测试 |
| 文档生成 | 从代码注释生成 HTML 文档 |
| 发布打包 | 发布到 crates.io |
| 工作空间 | 管理多 crate 项目 |
3.2 创建项目
cargo new
# 创建二进制项目(可执行文件)
cargo new hello_cargo
cd hello_cargo
# 创建库项目(library crate)
cargo new my_lib --lib
# 在当前目录初始化项目
cargo init
# 在当前目录初始化库项目
cargo init --lib
生成的项目结构
hello_cargo/
├── Cargo.toml # 项目清单文件
├── .gitignore # Git 忽略文件(自动创建)
└── src/
└── main.rs # 程序入口文件
src/main.rs 内容
fn main() {
println!("Hello, world!");
}
注意: cargo new 会自动初始化 Git 仓库并创建 .gitignore 文件。如果不需要,可以使用 cargo new --vcs=none。
3.3 Cargo.toml 详解
默认生成的 Cargo.toml
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2024"
[dependencies]
package 配置项
| 字段 | 说明 | 示例 |
|---|
name | 包名(在 crates.io 上的唯一标识) | "hello_cargo" |
version | 语义化版本号 | "0.1.0" |
edition | Rust 版本 | "2024" |
authors | 作者列表 | ["Name <email>"] |
description | 包描述(发布必需) | "A hello world project" |
license | 许可证 | "MIT" |
repository | 源码仓库 | "https://github.com/..." |
documentation | 文档链接 | "https://docs.rs/..." |
readme | README 文件 | "README.md" |
keywords | 关键字(最多5个) | ["hello", "world"] |
categories | 分类 | ["command-line-utilities"] |
exclude | 排除文件 | ["tests/*", "benches/*"] |
include | 包含文件 | ["src/**/*", "Cargo.toml"] |
publish | 是否发布 | false |
rust-version | 最低 Rust 版本 | "1.70" |
完整的 Cargo.toml 示例
[package]
name = "web-server"
version = "0.2.1"
edition = "2024"
authors = ["Zhang San <[email protected]>"]
description = "A simple HTTP web server"
license = "MIT OR Apache-2.0"
repository = "https://github.com/user/web-server"
readme = "README.md"
keywords = ["http", "server", "web"]
categories = ["web-programming::http-server"]
rust-version = "1.70"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
axum = "0.7"
tracing = "0.1"
tracing-subscriber = "0.3"
[dev-dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio-test = "0.4"
[build-dependencies]
cc = "1.0"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true
3.4 常用 Cargo 命令
构建与运行
# 编译项目(debug 模式)
cargo build
# 编译并运行
cargo run
# 仅检查代码(不生成二进制文件,比 build 快)
cargo check
# 编译 release 模式(启用优化)
cargo build --release
| 命令 | 输出目录 | 优化级别 | 编译速度 | 适用场景 |
|---|
cargo build | target/debug/ | 0(无优化) | 快 | 开发调试 |
cargo build --release | target/release/ | 3(最高优化) | 慢 | 生产部署 |
cargo run | target/debug/ | 0 | 快 | 快速测试 |
cargo run --release | target/release/ | 3 | 慢 | 性能测试 |
cargo check | — | — | 最快 | 代码检查 |
测试与文档
# 运行所有测试
cargo test
# 生成并打开文档
cargo doc --open
# 运行基准测试(nightly)
cargo bench
# 检查代码质量
cargo clippy
# 格式化代码
cargo fmt
依赖管理
# 添加依赖(Cargo 1.62+)
cargo add serde --features derive
cargo add tokio -F full
# 移除依赖
cargo remove serde
# 更新依赖
cargo update
# 查看依赖树
cargo tree
# 检查过期依赖
cargo install cargo-outdated
cargo outdated
3.5 第一个自定义程序
修改 main.rs
use std::io;
fn main() {
println!("=== 猜数字游戏 ===");
println!("我心里想了一个 1-100 的数字,猜猜看!");
// 使用固定种子的伪随机数(标准库无随机数,暂用简单方法)
let secret = 42; // 后续章节会介绍 rand crate
let mut attempts = 0;
loop {
println!("\n请输入你的猜测:");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("读取输入失败");
// 将字符串转换为数字
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("请输入一个有效的数字!");
continue;
}
};
attempts += 1;
match guess.cmp(&secret) {
std::cmp::Ordering::Less => println!("太小了!"),
std::cmp::Ordering::Greater => println!("太大了!"),
std::cmp::Ordering::Equal => {
println!("🎉 猜对了!用了 {} 次尝试。", attempts);
break;
}
}
}
}
运行:
添加第三方依赖
# Cargo.toml
[dependencies]
rand = "0.8"
use rand::Rng;
use std::io;
fn main() {
println!("=== 猜数字游戏 ===");
println!("我心里想了一个 1-100 的数字,猜猜看!");
let secret = rand::thread_rng().gen_range(1..=100);
let mut attempts = 0;
loop {
println!("\n请输入你的猜测:");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("读取输入失败");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("请输入一个有效的数字!");
continue;
}
};
attempts += 1;
match guess.cmp(&secret) {
std::cmp::Ordering::Less => println!("太小了!"),
std::cmp::Ordering::Greater => println!("太大了!"),
std::cmp::Ordering::Equal => {
println!("🎉 猜对了!用了 {} 次尝试。", attempts);
break;
}
}
}
}
注意: cargo run 首次运行时会自动下载并编译依赖,后续运行会使用缓存。
3.6 项目目录详解
完整的项目结构
hello_cargo/
├── Cargo.toml # 项目清单
├── Cargo.lock # 依赖锁定文件(自动生成)
├── src/
│ ├── main.rs # 二进制入口点
│ ├── lib.rs # 库入口点(可选)
│ ├── bin/ # 额外的二进制文件
│ │ └── tool.rs # → cargo run --bin tool
│ └── other_module.rs # 其他模块
├── tests/ # 集成测试
│ └── integration_test.rs
├── benches/ # 基准测试
│ └── benchmark.rs
├── examples/ # 示例代码
│ └── example.rs # → cargo run --example example
├── build.rs # 构建脚本(可选)
└── target/ # 构建输出(自动生成,勿手动编辑)
├── debug/
└── release/
Cargo.lock
- 二进制项目: 应提交到 Git(保证团队使用相同依赖版本)
- 库项目: 不应提交到 Git(让使用者决定具体版本)
target 目录
| 子目录 | 说明 |
|---|
target/debug/ | debug 构建输出 |
target/release/ | release 构建输出 |
target/doc/ | 生成的文档 |
target/package/ | 打包的 crate |
3.7 多二进制文件
方法一:src/bin/ 目录
# 项目结构
my_project/
├── Cargo.toml
└── src/
├── main.rs # 默认二进制
├── lib.rs # 库代码(可选)
└── bin/
├── server.rs # 额外二进制
└── client.rs # 额外二进制
// src/bin/server.rs
fn main() {
println!("服务器启动中...");
}
// src/bin/client.rs
fn main() {
println!("客户端连接中...");
}
# 运行特定二进制
cargo run --bin server
cargo run --bin client
# 列出所有二进制目标
cargo run --bin
方法二:在 Cargo.toml 中声明
[[bin]]
name = "server"
path = "src/bin/server.rs"
[[bin]]
name = "client"
path = "src/bin/client.rs"
3.8 条件编译与 Features
定义 Features
# Cargo.toml
[features]
default = ["json"]
json = ["dep:serde_json"]
xml = ["dep:quick-xml"]
full = ["json", "xml", "logging"]
logging = []
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", optional = true }
quick-xml = { version = "0.31", optional = true }
// src/main.rs
fn main() {
#[cfg(feature = "json")]
println!("JSON 支持已启用");
#[cfg(feature = "xml")]
println!("XML 支持已启用");
#[cfg(feature = "logging")]
println!("日志功能已启用");
#[cfg(not(any(feature = "json", feature = "xml")))]
println!("没有启用任何数据格式支持");
}
# 使用默认 features
cargo run
# 启用特定 features
cargo run --features "json,xml"
# 不使用默认 features
cargo run --no-default-features --features "xml"
3.9 业务场景示例:微服务项目结构
目录布局
microservice/
├── Cargo.toml # 工作空间
├── crates/
│ ├── common/ # 共享库
│ │ ├── Cargo.toml
│ │ └── src/lib.rs
│ ├── api-server/ # API 服务
│ │ ├── Cargo.toml
│ │ └── src/main.rs
│ └── worker/ # 后台任务
│ ├── Cargo.toml
│ └── src/main.rs
├── migrations/ # 数据库迁移
├── config/ # 配置文件
└── docker/ # Docker 配置
工作空间 Cargo.toml
[workspace]
members = [
"crates/common",
"crates/api-server",
"crates/worker",
]
resolver = "2"
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
子 crate 的 Cargo.toml
# crates/api-server/Cargo.toml
[package]
name = "api-server"
version = "0.1.0"
edition = "2024"
[dependencies]
common = { path = "../common" }
tokio = { workspace = true }
serde = { workspace = true }
axum = "0.7"
3.10 本章小结
| 要点 | 说明 |
|---|
| cargo new | 创建新项目,自动生成标准结构 |
| Cargo.toml | 项目清单,定义元数据和依赖 |
| cargo build/run/check | 构建、运行和检查项目 |
| Features | 条件编译,按需启用功能 |
| Cargo.lock | 锁定依赖版本,二进制项目应提交到 Git |
| 多二进制 | 使用 src/bin/ 目录或 [[bin]] 配置 |
| 工作空间 | 使用 [workspace] 管理多 crate 项目 |
扩展阅读
- Cargo 手册 — 官方 Cargo 文档
- Cargo.toml 参考 — 所有清单字段说明
- 语义化版本 — 语义化版本规范
- Crates.io 发布指南 — 如何发布 crate