
Rust 的瑞士军刀:从创建项目到发布 crate
你有没有经历过这样的绝望:
在 Rust 之前,C/C++ 程序员就是这样过来的……
Rust 的 Cargo 让这一切成为了历史。今天咱们就聊聊这个让依赖管理变得如此简单的神器。
Cargo 是 Rust 的包管理器 + 构建系统。它负责:
生活化类比:
想象你要做一顿大餐:
文件 | 作用 |
|---|---|
Cargo.toml | 项目配置文件(清单) |
Cargo.lock | 依赖锁定文件(精确版本) |
src/main.rs | 程序入口(可执行文件) |
src/lib.rs | 库入口(库项目) |
# 创建项目
cargo new my_project # 创建可执行文件
cargo new --lib my_lib # 创建库
# 构建和运行
cargo build # 编译
cargo run # 编译并运行
cargo check # 快速检查(不生成二进制)
# 依赖管理
cargo add <crate> # 添加依赖
cargo update # 更新依赖
cargo tree # 查看依赖树
# 测试和文档
cargo test# 运行测试
cargo doc # 生成文档
cargo doc --open # 生成并打开文档
# 发布
cargo publish # 发布到 crates.io
cargo package # 打包检查
# 创建可执行文件项目
cargo new hello_world
cd hello_world
# 项目结构
hello_world/
├── Cargo.toml
└── src/
└── main.rs
Cargo.toml 长这样:
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"
[dependencies]
# 依赖写在这里
方法 1:手动编辑 Cargo.toml
[package]
name = "my_app"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = "1.0" # 最新版本
serde_json = "1.0.108" # 指定版本
reqwest = { version = "0.11", features = ["json"] } # 带特性
方法 2:使用 cargo add(推荐)
cargo add serde
cargo add serde_json
cargo add reqwest --features json
说人话:
"1.0":兼容 1.0.x 的最新版本(语义化版本)"1.0.108":精确版本{ version = "0.11", features = ["json"] }:带特性的依赖[dependencies]
# 语义化版本(SemVer)
serde = "1.0" # >=1.0.0, <2.0.0
serde = "1.0.0" # >=1.0.0, <1.1.0
serde = "=1.0.108" # 精确 1.0.108
# 其他写法
serde = ">=1.0, <2.0" # 范围
serde = "*" # 任何版本(不推荐!)
语义化版本规则:
MAJOR.MINOR.PATCH(主版本。次版本。补丁版本)1.0 → 允许 1.0.0 到 1.99.991.0.0 → 允许 1.0.0 到 1.0.99// src/main.rs
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Person {
name: String,
age: u32,
}
fn main() {
let person = Person {
name: String::from("Alice"),
age: ,
};
// 序列化为 JSON
let json = serde_json::to_string(&person).unwrap();
println!("{}", json); // {"name":"Alice","age":30}
// 反序列化
let data = r#"{"name":"Bob","age":25}"#;
let person: Person = serde_json::from_str(data).unwrap();
println!("{:?}", person);
}
my_workspace/
├── Cargo.toml # 工作区配置
├── crate_a/
│ ├── Cargo.toml
│ └── src/
├── crate_b/
│ ├── Cargo.toml
│ └── src/
└── shared/
├── Cargo.toml
└── src/
工作区 Cargo.toml:
[workspace]
members = [
"crate_a",
"crate_b",
"shared",
]
[workspace.dependencies]
# 共享依赖(所有成员可用)
serde = "1.0"
tokio = "1.0"
成员 Cargo.toml:
[package]
name = "crate_a"
version = "0.1.0"
edition = "2021"
[dependencies]
# 继承工作区的依赖
serde = { workspace = true }
shared = { path = "../shared" } # 本地依赖
[features]
default = ["std"]
std = []
full = ["std", "async"]
async = ["tokio"]
[dependencies]
tokio = { version = "1.0", optional = true }
使用特性:
# 使用默认特性
cargo build
# 使用特定特性
cargo build --features full
# 不使用默认特性
cargo build --no-default-features --features async
# 登录 crates.io
cargo login <your_api_token>
# 打包检查
cargo package
# 发布
cargo publish
[package]
name = "my_crate"
version = "0.1.0"
edition = "2021"
description = "我的超棒库"
license = "MIT"
repository = "https://github.com/username/my_crate"
documentation = "https://docs.rs/my_crate"
# ❌ 可能冲突
[dependencies]
serde = "1.0.100"
some_crate = "0.1" # 这个 crate 依赖 serde 1.0.200
编译器在说什么人话?
"两个依赖需要不同版本的 serde,冲突了!"
解决方案:
# 更新依赖
cargo update
# 或者统一版本
cargo add serde@1.0.200
# ❌ 只提交了 Cargo.toml
git add Cargo.toml
git commit -m "添加依赖"
# ❌ 忘了 Cargo.lock
问题:
Cargo.lock(确保构建一致)Cargo.lock(让使用者决定版本)# crate_a/Cargo.toml
[dependencies]
crate_b = { path = "../crate_b" }
# crate_b/Cargo.toml
[dependencies]
crate_a = { path = "../crate_a" } # ❌ 循环依赖!
解决方案:
# ❌ 这样写有问题
[dependencies]
serde = { version = "1.0", features = ["derive"] }
# 其他 crate 可能也需要 serde,但没启用 derive 特性
解决方案:
确保所有需要的特性都启用了,或者用 workspace 统一管理。
[dependencies]
my_lib = { path = "../my_lib" }
问题:
正确做法:
[dependencies]
# 开发时用本地路径
my_lib = { path = "../my_lib" }
# 发布时改用 crates.io
# my_lib = "1.0"
或者用条件:
[dependencies]
my_lib = { path = "../my_lib", optional = true }
[package]
name = "web_server"
version = "0.1.0"
edition = "2021"
[dependencies]
# Web 框架
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }
# 数据库
sqlx = { version = "0.7", features = ["runtime-tokio-native-tls", "postgres"] }
# 序列化
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# 日志
tracing = "0.1"
tracing-subscriber = "0.3"
# 配置
dotenvy = "0.15"
[package]
name = "my_cli"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "my_cli"
path = "src/main.rs"
[dependencies]
# 参数解析
clap = { version = "4.0", features = ["derive"] }
# 文件操作
walkdir = "2.3"
# 进度条
indicatif = "0.17"
# 颜色输出
colored = "2.0"
[dev-dependencies]
# 测试用
assert_cmd = "2.0"
[package]
name = "my_library"
version = "0.1.0"
edition = "2021"
description = "一个超棒的工具库"
license = "MIT"
repository = "https://github.com/username/my_library"
documentation = "https://docs.rs/my_library"
[lib]
name = "my_lib"
path = "src/lib.rs"
[dependencies]
# 核心依赖
thiserror = "1.0" # 错误处理
[features]
default = ["std"]
std = []
async = ["tokio"]
[dependencies.tokio]
version = "1.0"
optional = true
[dev-dependencies]
# 测试依赖
tokio = { version = "1.0", features = ["full"] }
# 根 Cargo.toml
[workspace]
resolver = "2" # 使用 Rust 2021 的依赖解析器
members = [
"core",
"api",
"cli",
"web",
]
[workspace.package]
version = "0.1.0"
edition = "2021"
license = "MIT"
[workspace.dependencies]
# 共享依赖
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
tracing = "0.1"

金句回顾:
1.0 不等于 1.0.0,范围要搞清cargo package 先打包看看下篇预告:
代码写完了,怎么确保它是对的?测试啊!下篇聊聊Rust 的测试系统——单元测试、集成测试、文档测试,让你写出靠谱的代码!