
让 Rust 结构体学会"变身"——serde 从入门到性能优化
你有没有想过,Rust 结构体怎么变成 JSON 发给前端?或者从数据库读出来的数据怎么变成你的结构体?
答案:序列化与反序列化。
生活化类比:
今天咱们聊聊 Rust 的序列化神器——serde。这玩意儿强大到你无法想象,不仅能处理 JSON,还能处理 YAML、TOML、MessagePack、甚至二进制格式。
吐槽: 我第一次用 serde 的时候,看到 #[derive(Serialize, Deserialize)] 就能自动序列化,心想"这魔法也太离谱了吧"。用久了发现:真香!
serde = Serialization + Deserialization
它是 Rust 生态中最流行的序列化框架,特点:
格式 | Crate | 用途 |
|---|---|---|
JSON | serde_json | Web API、配置文件 |
YAML | serde_yaml | 配置文件 |
TOML | toml | Cargo.toml 就是这格式 |
MessagePack | rmp-serde | 二进制、高效 |
BSON | bson | MongoDB |
CBOR | serde_cbor | 物联网、高效 |
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
就这么简单!serde 自动生成序列化和反序列化代码。
类比: 这就像给结构体装了个"变身器",一键变 JSON。
cargo new my_serde_app
cd my_serde_app
修改 Cargo.toml:
[package]
name = "my_serde_app"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.9"
toml = "0.8"
use serde::{Serialize, Deserialize};
use serde_json;
#[derive(Serialize, Deserialize, Debug)]
struct User {
id: u32,
name: String,
email: String,
age: Option<u32>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建结构体
let user = User {
id: ,
name: "Larry".to_string(),
email: "larry@example.com".to_string(),
age: Some(),
};
// 序列化:Rust → JSON
let json = serde_json::to_string(&user)?;
println!("序列化:{}", json);
// 输出:{"id":1,"name":"Larry","email":"larry@example.com","age":25}
// 格式化输出(带缩进)
let json_pretty = serde_json::to_string_pretty(&user)?;
println!("格式化:\n{}", json_pretty);
/*
输出:
{
"id": 1,
"name": "Larry",
"email": "larry@example.com",
"age": 25
}
*/
// 反序列化:JSON → Rust
let json_str = r#"{"id":2,"name":"Alice","email":"alice@example.com","age":30}"#;
let user2: User = serde_json::from_str(json_str)?;
println!("反序列化:{:?}", user2);
Ok(())
}
运行:
cargo run
有时候 JSON 字段名和 Rust 结构体字段名不一样,怎么办?
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct User {
id: u32,
// 自定义 JSON 字段名
#[serde(rename = "userName")]
name: String,
#[serde(rename = "emailAddress")]
email: String,
// 处理不同命名风格
#[serde(rename = "created_at")]// snake_case → snake_case
created_at: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let user = User {
id: ,
name: "Larry".to_string(),
email: "larry@example.com".to_string(),
created_at: "2024-01-01".to_string(),
};
let json = serde_json::to_string(&user)?;
println!("{}", json);
// 输出:{"id":1,"userName":"Larry","emailAddress":"larry@example.com","created_at":"2024-01-01"}
Ok(())
}
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Config {
name: String,
// 可选字段
#[serde(default)]
version: String, // 缺失时用 Default::default() (空字符串)
// 自定义默认值
#[serde(default = "default_port")]
port: u32,
// 跳过序列化
#[serde(skip_serializing)]
password: String,
// 跳过反序列化
#[serde(skip_deserializing)]
computed_field: String,
}
fn default_port() -> u32 {
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// JSON 缺失字段也能反序列化
let json_str = r#"{"name": "my-app"}"#;
let config: Config = serde_json::from_str(json_str)?;
println!("name: {}", config.name); // "my-app"
println!("version: {}", config.version); // "" (默认)
println!("port: {}", config.port); // 8080 (自定义默认)
// 序列化时 password 不会出现
let config = Config {
name: "my-app".to_string(),
version: "1.0.0".to_string(),
port: ,
password: "secret".to_string(),
computed_field: "computed".to_string(),
};
let json = serde_json::to_string(&config)?;
println!("{}", json);
// 输出:{"name":"my-app","version":"1.0.0","port":3000,"computed_field":"computed"}
// 注意:password 不在输出中
Ok(())
}
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug)]
struct Post {
id: u32,
title: String,
tags: Vec<String>,
metadata: HashMap<String, String>,
author: Author,
}
#[derive(Serialize, Deserialize, Debug)]
struct Author {
name: String,
email: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut metadata = HashMap::new();
metadata.insert("views".to_string(), "1000".to_string());
metadata.insert("likes".to_string(), "50".to_string());
let post = Post {
id: ,
title: "Rust 序列化指南".to_string(),
tags: vec!["rust".to_string(), "serde".to_string()],
metadata,
author: Author {
name: "Larry".to_string(),
email: "larry@example.com".to_string(),
},
};
let json = serde_json::to_string_pretty(&post)?;
println!("{}", json);
Ok(())
}
输出:
{
"id": ,
"title": "Rust 序列化指南",
"tags": ["rust", "serde"],
"metadata": {
"views": "1000",
"likes": "50"
},
"author": {
"name": "Larry",
"email": "larry@example.com"
}
}
有时候默认行为不够用,得自己实现。
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use serde::de::{self, Visitor};
use std::fmt;
// 自定义类型
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
// 序列化:变成 [x, y] 数组
impl Serialize for Point {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some())?;
seq.serialize_element(&self.x)?;
seq.serialize_element(&self.y)?;
seq.end()
}
}
// 反序列化:从 [x, y] 数组解析
impl<'de> Deserialize<'de> for Point {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct PointVisitor;
impl<'de> Visitor<'de> for PointVisitor {
type Value = Point;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an array of two integers")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Point, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let x = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(, &self))?;
let y = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(, &self))?;
Ok(Point { x, y })
}
}
deserializer.deserialize_seq(PointVisitor)
}
}
#[derive(Serialize, Deserialize, Debug)]
struct Shape {
name: String,
center: Point,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let shape = Shape {
name: "circle".to_string(),
center: Point { x: , y: },
};
let json = serde_json::to_string(&shape)?;
println!("{}", json);
// 输出:{"name":"circle","center":[10,20]}
let json_str = r#"{"name":"rectangle","center":[5,15]}"#;
let shape2: Shape = serde_json::from_str(json_str)?;
println!("{:?}", shape2);
Ok(())
}
use serde::{Serialize, Deserialize};
use serde_json;
#[derive(Serialize, Deserialize, Debug)]
struct LargeData {
id: u64,
name: String,
data: Vec<u8>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = LargeData {
id: ,
name: "test".to_string(),
data: vec![0u8; ], // 1MB 数据
};
// 方式 1:to_string (返回 String)
let start = std::time::Instant::now();
let _json = serde_json::to_string(&data)?;
println!("to_string: {:?}", start.elapsed());
// 方式 2:to_vec (返回 Vec<u8>,更快)
let start = std::time::Instant::now();
let _json_bytes = serde_json::to_vec(&data)?;
println!("to_vec: {:?}", start.elapsed());
// 方式 3:to_writer (直接写入 IO,零拷贝)
let mut buffer = Vec::new();
let start = std::time::Instant::now();
serde_json::to_writer(&mut buffer, &data)?;
println!("to_writer: {:?}", start.elapsed());
Ok(())
}
性能对比:
to_string - 最慢,多了 UTF-8 转换to_vec - 快,直接返回字节to_writer - 最快,零拷贝建议: 大数据用 to_writer 或 to_vec,小数据随意。
derive 特性错误:
serde = "1.0" # ❌ 没有 derive
编译错误:
error: cannot find derive macro `Serialize` in this scope
解决:
serde = { version = "1.0", features = ["derive"] }
错误:
#[derive(Serialize)]
struct Data {
value: std::sync::Mutex<i32>, // ❌ Mutex 不支持 Serialize
}
解决:
use serde::Serialize;
#[derive(Serialize)]
struct Data {
value: i32, // 用基本类型
}
或者手动实现 Serialize。
错误:
#[derive(Serialize, Deserialize)]
struct Data<'a> {
value: &'a str, // ❌ 带生命周期的引用
}
解决:
#[derive(Serialize, Deserialize)]
struct Data {
value: String, // ✅ 用拥有的类型
}
现象: JSON 里浮点数精度丢失
解决: 用 serde_with crate 处理特殊格式:
serde_with = "3.0"
use serde_with::serde_as;
use serde_with::DisplayFromStr;
#[serde_as]
#[derive(Serialize, Deserialize)]
struct Data {
#[serde_as(as = "DisplayFromStr")]
value: f64, // 用字符串表示,避免精度丢失
}
use serde::{Serialize, Deserialize};
use std::fs;
use std::path::Path;
// 主配置
#[derive(Serialize, Deserialize, Debug)]
struct Config {
app: AppConfig,
database: DatabaseConfig,
logging: LoggingConfig,
}
#[derive(Serialize, Deserialize, Debug)]
struct AppConfig {
name: String,
version: String,
port: u16,
#[serde(default = "default_workers")]
workers: u32,
}
#[derive(Serialize, Deserialize, Debug)]
struct DatabaseConfig {
host: String,
port: u16,
username: String,
#[serde(skip_serializing)]// 不序列化密码
password: String,
database: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct LoggingConfig {
level: LogLevel,
file: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "lowercase")]
enum LogLevel {
Debug,
Info,
Warn,
Error,
}
fn default_workers() -> u32 {
}
impl Config {
// 从文件加载配置
fn load(path: &Path) -> Result<Self, Box<dyn std::error::Error>> {
let content = fs::read_to_string(path)?;
// 支持多种格式
let config: Config = if path.extension().map_or(false, |e| e == "json") {
serde_json::from_str(&content)?
} else if path.extension().map_or(false, |e| e == "yaml") {
serde_yaml::from_str(&content)?
} else if path.extension().map_or(false, |e| e == "toml") {
toml::from_str(&content)?
} else {
return Err("Unsupported config format".into());
};
Ok(config)
}
// 保存配置到文件
fn save(&self, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let content = if path.extension().map_or(false, |e| e == "json") {
serde_json::to_string_pretty(self)?
} else if path.extension().map_or(false, |e| e == "yaml") {
serde_yaml::to_string(self)?
} else if path.extension().map_or(false, |e| e == "toml") {
toml::to_string_pretty(self)?
} else {
return Err("Unsupported config format".into());
};
fs::write(path, content)?;
Ok(())
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建配置
let config = Config {
app: AppConfig {
name: "my-app".to_string(),
version: "1.0.0".to_string(),
port: ,
workers: ,
},
database: DatabaseConfig {
host: "localhost".to_string(),
port: ,
username: "admin".to_string(),
password: "secret".to_string(),
database: "mydb".to_string(),
},
logging: LoggingConfig {
level: LogLevel::Debug,
file: Some("app.log".to_string()),
},
};
// 保存配置
config.save(Path::new("config.yaml"))?;
println!("✅ 配置已保存");
// 加载配置
let loaded = Config::load(Path::new("config.yaml"))?;
println!("✅ 配置已加载:{:?}", loaded);
Ok(())
}
配置文件示例 (config.yaml):
app:
name: my-app
version: 1.0.0
port:
workers:
database:
host: localhost
port:
username: admin
database: mydb
logging:
level: debug
file: app.log

核心要点:
#[derive] 搞定大部分场景rename、default、skip 处理各种边界情况to_writer,避免不必要的转换Serialize/Deserialize金句:
序列化就是给结构体装变身器,一键变 JSON。
默认值处理得好,用户少填好多空。
大数据别用 to_string,to_writer 才是王道。
下篇预告:
程序跑起来了,怎么知道它有没有问题?日志!下篇咱们聊聊 日志与配置,用 tracing 记录程序行为,用 config 管理配置,让你的程序可观测、可调试!