serde 序列化框架
问题
serde 是如何工作的?为什么它是零成本的?
答案
serde 是 Rust 的序列化/反序列化框架,通过 derive 宏在编译时生成代码,运行时零开销。
基础用法
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
name: String,
age: u32,
#[serde(default)] // 缺失时使用默认值
email: Option<String>,
#[serde(rename = "created_at")] // 字段重命名
created: String,
#[serde(skip)] // 跳过此字段
cache: Vec<u8>,
}
fn main() -> serde_json::Result<()> {
// 序列化
let user = User {
name: "Alice".into(),
age: 30,
email: Some("alice@example.com".into()),
created: "2024-01-01".into(),
cache: vec![],
};
let json = serde_json::to_string_pretty(&user)?;
// 反序列化
let parsed: User = serde_json::from_str(&json)?;
println!("{:?}", parsed);
Ok(())
}
常用属性
| 属性 | 位置 | 作用 |
|---|---|---|
#[serde(rename = "...")] | 字段 | 重命名字段 |
#[serde(default)] | 字段/结构体 | 缺失时用默认值 |
#[serde(skip)] | 字段 | 跳过此字段 |
#[serde(flatten)] | 字段 | 扁平化嵌套结构 |
#[serde(rename_all = "camelCase")] | 结构体 | 统一字段命名风格 |
#[serde(tag = "type")] | 枚举 | 内部标签形式 |
#[serde(untagged)] | 枚举 | 无标签形式 |
枚举序列化
#[derive(Serialize, Deserialize)]
#[serde(tag = "type", content = "data")] // 邻接标签
enum Message {
Text(String),
Image { url: String, width: u32 },
Ping,
}
// 序列化结果:
// {"type": "Text", "data": "hello"}
// {"type": "Image", "data": {"url": "...", "width": 100}}
// {"type": "Ping"}
支持的格式
serde 本身是格式无关的,通过不同 crate 支持各种格式:
| 格式 | crate | 用途 |
|---|---|---|
| JSON | serde_json | API、配置 |
| TOML | toml | Cargo.toml 等配置 |
| YAML | serde_yaml | K8s 配置等 |
| MessagePack | rmp-serde | 高效二进制 |
| Bincode | bincode | 高性能二进制 |
| RON | ron | Rust 风格配置 |
常见面试问题
Q1: serde 为什么高效?
答案:
serde 通过 #[derive] 宏在编译时生成序列化代码,不使用反射。编译后的代码与手写的序列化逻辑一样快,且可被编译器内联优化。
Q2: #[serde(tag)] 的几种枚举表示有什么区别?
答案:
| 标签模式 | 属性 | JSON 输出 |
|---|---|---|
| 外部标签(默认) | 无 | {"Text": "hello"} |
| 内部标签 | tag = "type" | {"type": "Text", ...} |
| 邻接标签 | tag = "t", content = "c" | {"t": "Text", "c": "hello"} |
| 无标签 | untagged | "hello" |
API 设计中最常用内部标签,与 TypeScript 的可辨识联合类似。
Q3: 如何自定义序列化逻辑?
答案:
use serde::{Serializer, Deserializer};
#[derive(Serialize, Deserialize)]
struct Config {
#[serde(serialize_with = "serialize_duration")]
timeout: std::time::Duration,
}
fn serialize_duration<S: Serializer>(
dur: &std::time::Duration,
s: S,
) -> Result<S::Ok, S::Error> {
s.serialize_u64(dur.as_secs())
}