首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >42-Rust 教程 - 序列化与反序列化

42-Rust 教程 - 序列化与反序列化

作者头像
LarryLan
发布2026-06-30 14:35:22
发布2026-06-30 14:35:22
990
举报

序列化与反序列化

让 Rust 结构体学会"变身"——serde 从入门到性能优化

🎬 引入

你有没有想过,Rust 结构体怎么变成 JSON 发给前端?或者从数据库读出来的数据怎么变成你的结构体?

答案:序列化与反序列化。

  • 序列化:Rust 结构体 → JSON/其他格式
  • 反序列化:JSON/其他格式 → Rust 结构体

生活化类比:

  • 序列化 = 打包行李,把衣服(结构体)装进箱子(JSON)
  • 反序列化 = 拆行李,从箱子(JSON)拿出衣服(结构体)

今天咱们聊聊 Rust 的序列化神器——serde。这玩意儿强大到你无法想象,不仅能处理 JSON,还能处理 YAML、TOML、MessagePack、甚至二进制格式。

吐槽: 我第一次用 serde 的时候,看到 #[derive(Serialize, Deserialize)] 就能自动序列化,心想"这魔法也太离谱了吧"。用久了发现:真香!

📌 核心概念

serde 是啥?

serde = Serialization + Deserialization

它是 Rust 生态中最流行的序列化框架,特点:

  • - 零拷贝优化
  • 通用 - 支持 20+ 种格式
  • 类型安全 - 编译期检查
  • 零成本抽象 - 几乎没性能损失

支持的数据格式

格式

Crate

用途

JSON

serde_json

Web API、配置文件

YAML

serde_yaml

配置文件

TOML

toml

Cargo.toml 就是这格式

MessagePack

rmp-serde

二进制、高效

BSON

bson

MongoDB

CBOR

serde_cbor

物联网、高效

派生宏:Serialize 和 Deserialize

代码语言:javascript
复制
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct User {
    id: u32,
    name: String,
    email: String,
}

就这么简单!serde 自动生成序列化和反序列化代码。

类比: 这就像给结构体装了个"变身器",一键变 JSON。

💻 代码示例

第一步:创建项目 + 安装依赖

代码语言:javascript
复制
cargo new my_serde_app
cd my_serde_app

修改 Cargo.toml

代码语言:javascript
复制
[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"

第二步:基础 JSON 序列化

代码语言:javascript
复制
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(())
}

运行:

代码语言:javascript
复制
cargo run

第三步:自定义字段名

有时候 JSON 字段名和 Rust 结构体字段名不一样,怎么办?

代码语言:javascript
复制
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(())
}

第四步:处理缺失字段和默认值

代码语言:javascript
复制
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(())
}

第五步:处理复杂类型

代码语言:javascript
复制
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(())
}

输出:

代码语言:javascript
复制
{
  "id": ,
  "title": "Rust 序列化指南",
  "tags": ["rust", "serde"],
  "metadata": {
    "views": "1000",
    "likes": "50"
  },
  "author": {
    "name": "Larry",
    "email": "larry@example.com"
  }
}

第六步:自定义序列化和反序列化

有时候默认行为不够用,得自己实现。

代码语言:javascript
复制
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(())
}

第七步:性能优化

代码语言:javascript
复制
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_writerto_vec,小数据随意。

🐛 常见坑点

坑 1:忘记加 derive 特性

错误:

代码语言:javascript
复制
serde = "1.0"  # ❌ 没有 derive

编译错误:

代码语言:javascript
复制
error: cannot find derive macro `Serialize` in this scope

解决:

代码语言:javascript
复制
serde = { version = "1.0", features = ["derive"] }

坑 2:字段类型不支持序列化

错误:

代码语言:javascript
复制
#[derive(Serialize)]
struct Data {
    value: std::sync::Mutex<i32>, // ❌ Mutex 不支持 Serialize
}

解决:

代码语言:javascript
复制
use serde::Serialize;

#[derive(Serialize)]
struct Data {
    value: i32,  // 用基本类型
}

或者手动实现 Serialize。

坑 3:生命周期问题

错误:

代码语言:javascript
复制
#[derive(Serialize, Deserialize)]
struct Data<'a> {
    value: &'a str,  // ❌ 带生命周期的引用
}

解决:

代码语言:javascript
复制
#[derive(Serialize, Deserialize)]
struct Data {
    value: String,  // ✅ 用拥有的类型
}

坑 4:浮点数精度问题

现象: JSON 里浮点数精度丢失

解决:serde_with crate 处理特殊格式:

代码语言:javascript
复制
serde_with = "3.0"
代码语言:javascript
复制
use serde_with::serde_as;
use serde_with::DisplayFromStr;

#[serde_as]
#[derive(Serialize, Deserialize)]
struct Data {
    #[serde_as(as = "DisplayFromStr")]
    value: f64,  // 用字符串表示,避免精度丢失
}

🎯 实战案例

案例:配置文件管理

代码语言:javascript
复制
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):

代码语言:javascript
复制
app:
  name: my-app
  version: 1.0.0
  port: 
  workers: 

database:
  host: localhost
  port: 
  username: admin
  database: mydb

logging:
  level: debug
  file: app.log

🧠 思维导图

42-序列化与反序列化
42-序列化与反序列化

📝 小结

核心要点:

  1. serde 是序列化神器 - 一个 #[derive] 搞定大部分场景
  2. 属性配置很强大 - renamedefaultskip 处理各种边界情况
  3. 支持多种格式 - JSON、YAML、TOML、二进制,一套代码通吃
  4. 性能有讲究 - 大数据用 to_writer,避免不必要的转换
  5. 自定义序列化 - 特殊类型可以手动实现 Serialize/Deserialize

金句:

序列化就是给结构体装变身器,一键变 JSON。

默认值处理得好,用户少填好多空。

大数据别用 to_string,to_writer 才是王道。

下篇预告:

程序跑起来了,怎么知道它有没有问题?日志!下篇咱们聊聊 日志与配置,用 tracing 记录程序行为,用 config 管理配置,让你的程序可观测、可调试!

🔗 参考资料

  • serde 官方文档
  • serde_json 文档
  • serde 属性详解
  • 自定义序列化
  • 性能优化指南
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-06-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Larry的Hub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 序列化与反序列化
    • 🎬 引入
    • 📌 核心概念
      • serde 是啥?
      • 支持的数据格式
      • 派生宏:Serialize 和 Deserialize
    • 💻 代码示例
      • 第一步:创建项目 + 安装依赖
      • 第二步:基础 JSON 序列化
      • 第三步:自定义字段名
      • 第四步:处理缺失字段和默认值
      • 第五步:处理复杂类型
      • 第六步:自定义序列化和反序列化
      • 第七步:性能优化
    • 🐛 常见坑点
      • 坑 1:忘记加 derive 特性
      • 坑 2:字段类型不支持序列化
      • 坑 3:生命周期问题
      • 坑 4:浮点数精度问题
    • 🎯 实战案例
      • 案例:配置文件管理
    • 🧠 思维导图
    • 📝 小结
    • 🔗 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档