楼层: 首页/ 软件技术/ Rust 后端技术栈/ 序列化、配置、日志、错误处理
04

序列化、配置、日志、错误处理

serde, config-rs, tracing, clap

后端不只是写路由。配置怎么从文件+环境变量读?日志怎么结构化?命令行参数怎么解析?错误怎么分层?这一章把这些"胶水"讲清楚——它们决定了你的服务是能维护的产品,还是一堆脚本。

serde:Rust 的序列化标准

serde 是 Rust 生态最核心的 crate 之一——它定义了序列化/反序列化的抽象,具体格式由 serde_json、serde_yaml、serde_toml 提供。你只要给结构体 derive Serialize, Deserialize,剩下的事 serde 全包了。

use serde::{Serialize, Deserialize}; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] // JSON 里用 camelCase,Rust 里用 snake_case struct User { id: u64, username: String, #[serde(default)] // 字段缺失时用 Default email: String, #[serde(skip_serializing_if = "Option::is_none")] phone: Option<String>, // None 时不输出这个字段 } let u = User { id: 1, username: "tom".into(), email: "".into(), phone: None }; let json = serde_json::to_string_pretty(&u)?; println!("{json}");
{ "id": 1, "username": "tom", "email": "" }

配置管理:config-rs + dotenvy

对标 Spring Boot 的 @ConfigurationProperties:config-rs 支持多层配置(默认值 + 配置文件 + 环境变量 + 命令行),dotenvy 负责读 .env 文件。

use serde::Deserialize; #[derive(Debug, Deserialize, Clone)] struct AppConfig { database_url: String, port: u16, log_level: String, } let cfg = config::Config::builder() .add_source(config::File::with_name("config/default")) // 默认文件 .add_source(config::Environment::default().separator("__")) // 环境变量 APP__PORT .build()?; let cfg: AppConfig = cfg.try_deserialize()?; println!("监听端口 {}", cfg.port);

日志:tracing 是 Rust 后端的事实标准

别再用 println! 打日志了。tracing 是 Tokio 团队出的结构化日志库,支持 span(一段操作的上下文)、结构化字段、异步不阻塞。它和 tracing-subscriber 配合,就是 Rust 版的 Logback + MDC。

use tracing::{info, warn, error, instrument}; // 初始化:RUST_LOG=info,sqlx=warn 控制级别 tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .init(); // #[instrument] 自动把函数参数记入 span #[instrument] async fn login(user: &str) { info!(user, "用户开始登录"); if user.is_empty() { warn!("空用户名"); } error!("登录失败示例"); }
INFO login{user=tom}: 用户开始登录 WARN login{user=tom}: 空用户名

错误分层:anyhow vs thiserror

库用在哪特点
anyhow应用层(main、handler)一个 anyhow::Error 装所有错误,灵活,带上下文 .context("...")
thiserror库层(定义错误枚举)derive Error,给你的库定义结构化错误类型,调用方能 match

口诀:库作者用 thiserror 定义枚举,应用作者用 anyhow 随便装。Axum 的 AppError 用 thiserror,main 函数返回 anyhow::Result<()>。

命令行参数:clap

use clap::Parser; #[derive(Parser, Debug)] #[command(name = "myapp", version, about = "Rust 后端服务")] struct Cli { // --host 0.0.0.0 #[arg(long, default_value = "0.0.0.0")] host: String, // --port 8080 #[arg(long, default_value_t = 3000)] port: u16, // 子命令:migrate / serve #[command(subcommand)] cmd: Cmd, } #[derive(clap::Subcommand, Debug)] enum Cmd { Serve, Migrate, }
记
本章小结

① 序列化用 serde + derive,配置用 config-rs 多层叠加。

② 日志用 tracing,错误用 thiserror(库)+ anyhow(应用),CLI 用 clap derive。