前置知识: Rust

常用生态与实战

3 min高级

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 一发即验。