常用生态与实战
axum Web 服务、serde 序列化、clap CLI、tracing 日志与项目实战
1. 生态全景
Rust 服务端生态已相当成熟,四个核心 crate 覆盖了 Web 服务的主要需求:
| crate | 定位 | 类比 |
|---|---|---|
| axum | 异步 Web 框架 | Express / Spring Boot |
| serde | 序列化/反序列化 | Jackson / JSON.stringify |
| clap | 命令行参数解析 | argparse / commander |
| tracing | 结构化日志与追踪 | slf4j / pino |
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
clap = { version = "4", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = "0.3"
2. axum:Web 服务
2.1 最小服务
use axum::{routing::get, Router};
async fn hello() -> &'static str {
"Hello, World!"
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(hello))
.route("/health", get(|| async { "ok" }));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
println!("server listening on :3000");
axum::serve(listener, app).await.unwrap();
}
讲解:Router::new() 建路由,.route(path, get(handler)) 注册 GET 处理器;axum 处理器支持多种返回类型(&str、String、Json、Result 等)。axum::serve 启动 HTTP 服务。
2.2 JSON API 与路径参数
use axum::{extract::Path, Json, Router, routing::post};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct User {
id: u32,
name: String,
}
async fn get_user(Path(id): Path<u32>) -> Json<User> {
Json(User { id, name: format!("user{id}") })
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
}
async fn create_user(Json(req): Json<CreateUser>) -> Json<User> {
Json(User { id: 1, name: req.name })
}
fn main() {
let app = Router::new()
.route("/users/{id}", axum::routing::get(get_user))
.route("/users", post(create_user));
// ... 绑定端口并 serve(省略)
}
讲解:Path<T> 提取路径参数(axum 0.8 用 {id} 语法),Json<T> 自动反序列化请求体、序列化响应体——serde 是幕后功臣。
2.3 全局状态(State)
use std::sync::Arc;
use axum::{extract::State, Router, routing::get};
#[derive(Clone)]
struct AppState {
config_version: u32,
}
async fn version(State(state): State<AppState>) -> String {
format!("v{}", state.config_version)
}
fn main() {
let state = AppState { config_version: 3 };
let app = Router::new()
.route("/version", get(version))
.with_state(state); // 注入全局状态
// ... serve
}
讲解:State<T> 提取全局共享状态,处理器之间共享配置、数据库连接池等;要求 T: Clone(内部通常是 Arc)。
3. serde:序列化
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Config {
host: String,
port: u16,
#[serde(default)]
debug: bool, // 缺省字段给默认值
#[serde(rename = "db_name")]
db_name: String, // JSON 字段名映射
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let json = r#"{"host":"127.0.0.1","port":5432,"db_name":"fandex"}"#;
let cfg: Config = serde_json::from_str(json)?; // JSON -> 结构体
println!("{cfg:?}");
let out = serde_json::to_string_pretty(&cfg)?; // 结构体 -> JSON
println!("{out}");
Ok(())
}
讲解:Serialize/Deserialize 派生宏自动生成转换代码。常用属性:#[serde(default)] 容错缺省字段、#[serde(rename)] 映射字段名、#[serde(skip)] 跳过字段。serde 不只支持 JSON,还支持 toml、yaml、二进制(bincode/postcard)等格式——cargo add toml 后即可 toml::from_str。
4. clap:命令行工具
use clap::Parser;
#[derive(Parser, Debug)]
#[command(name = "fandex", version, about = "FANDEX 命令行工具")]
struct Cli {
/// 输入文件路径
#[arg(short, long)]
input: String,
/// 输出文件路径
#[arg(short, long, default_value = "out.txt")]
output: String,
/// 是否启用详细模式
#[arg(short, long, default_value_t = false)]
verbose: bool,
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
if cli.verbose {
println!("详细模式已开启");
}
}
讲解:结构体即 CLI 定义:字段注释成为 —help 帮助文本,#[arg(short, long)] 生成 -i/--input,默认值、布尔开关一行搞定。运行 cargo run -- -i data.txt --verbose 测试。
5. tracing:日志与追踪
use tracing::{info, warn, error, debug};
fn main() {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
let user = "fanquanpp";
info!(user, "用户登录"); // 结构化字段
debug!("开始处理请求");
warn!(reason = "重试次数超限", "请求失败");
error!(code = 500, "服务器内部错误");
}
讲解:tracing 的日志带结构化字段(key = value),便于机器解析与链路追踪;tracing_subscriber 配置输出格式与级别。设置环境变量控制级别:RUST_LOG=info cargo run。
6. 项目实战:极简待办 API
把上述生态组合成一个完整可运行项目:
use std::sync::Arc;
use axum::{extract::State, routing::get, Json, Router};
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
#[derive(Clone, Serialize, Deserialize, Debug)]
struct Todo {
id: u32,
title: String,
done: bool,
}
#[derive(Default)]
struct Store {
items: Vec<Todo>,
next_id: u32,
}
type Db = Arc<Mutex<Store>>;
#[derive(Deserialize)]
struct NewTodo {
title: String,
}
async fn list(State(db): State<Db>) -> Json<Vec<Todo>> {
let store = db.lock().await;
Json(store.items.clone())
}
async fn add(State(db): State<Db>, Json(req): Json<NewTodo>) -> Json<Todo> {
let mut store = db.lock().await;
store.next_id += 1;
let todo = Todo { id: store.next_id, title: req.title, done: false };
store.items.push(todo.clone());
tracing::info!(id = todo.id, "新增待办");
Json(todo)
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt().init();
let db: Db = Arc::new(Mutex::new(Store::default()));
let app = Router::new()
.route("/todos", get(list).post(add))
.with_state(db);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
tracing::info!("服务已启动:http://localhost:3000");
axum::serve(listener, app).await.unwrap();
}
讲解:这是完整的生产骨架——Arc<Mutex<Store>> 共享内存状态、axum 路由、serde 自动序列化、tracing 打日志。测试:
curl -X POST http://localhost:3000/todos -H "Content-Type: application/json" -d '{"title":"学习 Rust"}'
curl http://localhost:3000/todos
7. 实战进阶建议
| 需求 | 推荐 crate |
|---|---|
| 数据库(SQL) | sqlx(异步、编译期检查 SQL) |
| 数据库(ORM) | sea-orm / diesel |
| 配置管理 | config(TOML/JSON/环境变量合并) |
| HTTP 客户端 | reqwest |
| 密码学/鉴权 | argon2、jsonwebtoken |
| 单元测试增强 | proptest(属性测试)、criterion(基准) |
| 部署 | Docker 多阶段构建,镜像约 10MB |
部署提示:Rust 静态编译,cargo build --release 后单二进制即可部署;配合 docker run 或 systemd 即可上线。
8. 小结
axum(路由)+ serde(数据)+ clap(CLI)+ tracing(日志)构成了 Rust 服务端开发的核心组合。本篇的待办 API 演示了从零搭建一个可运行服务的完整流程。至此,从环境搭建到生态实战的 Rust 主线学习路径已全部完成——用实战项目巩固知识,是进阶的不二法门。
回顾完整学习路径:Rust 是什么 → 语言概述 → 环境搭建 → 基础语法 → 所有权与借用 → 借用检查器报错实战 → 结构体/枚举/模式匹配 → 错误处理 → 集合与迭代器 → 泛型与 Trait → 测试与调试 → 异步与 Tokio → 常用生态与实战(本篇)。之后的进阶篇章:智能指针、宏编程、并发编程、生命周期深入、闭包与 Fn 特征、Cargo 进阶、Unsafe Rust。每一环都为下一环铺路,建议按顺序学习并完成各章练习。
一句话记忆:Rust 服务端四件套——“axum 管路由、serde 管数据、clap 管参数、tracing 管日志”;用它们组合出的待办 API 是生产级骨架(
Arc<Mutex<Store>>+ Router + Json),curl 一发即验。