Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.3. API设计原则之不意外性(unsurprising) Pt.3:实现serde下的Serialize和Deserialize trait、不建议实现Copy trait

2.3.1. 建议实现serde下的SerializeDeserialize trait

serde是Rust中用于序列化(serialization)和反序列化(deserialization) 的核心库:

  • 序列化(Serialization):把Rust结构体或枚举转换为JSON、YAML等格式的字符串或二进制数据。
  • 反序列化(Deserialization):从JSON、YAML等格式的字符串或二进制数据解析回Rust结构体或枚举。

SerializeDeserialize都是serde库下的trait。

Serialize trait

Serialize trait允许一个类型转换为可序列化的数据格式(如JSON、YAML、TOML)。

其主要方法有:

  • serialize_bool
  • serialize_i32
  • serialize_str
  • serialize_struct

这些是 Serialize 实现会调用的 Serializer(及相关)trait 上的方法;Serialize trait 本身只要求实现 serialize

其定义是:

#![allow(unused)]
fn main() {
pub trait Serialize {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer;
}
}

我们用一个例子来介绍如何手动实现Serialize trait:

#![allow(unused)]
fn main() {
use serde::ser::{Serialize, SerializeStruct, Serializer};

struct Point {
    x: i32,
    y: i32,
}

impl Serialize for Point {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state = serializer.serialize_struct("Point", 2)?;
        state.serialize_field("x", &self.x)?;
        state.serialize_field("y", &self.y)?;
        state.end()
    }
}
}
  • serializer.serialize_struct("Point", 2)?创建一个结构体序列化对象,2 代表字段数量
  • state.serialize_field("x", &self.x)?依次序列化结构体字段
  • state.end()结束序列化

Deserialize trait

Deserialize trait允许从各种数据格式解析Rust类型。

其主要方法有:

  • deserialize_bool
  • deserialize_i32
  • deserialize_string
  • deserialize_struct

这些是 Deserialize 实现通过 Visitor 驱动的 Deserializer(及相关)trait 上的方法;Deserialize trait 本身只要求实现 deserialize

其定义是:

#![allow(unused)]
fn main() {
pub trait Deserialize<'de>: Sized {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>;
}
}
  • deserialize方法的作用是:使用deserializer从数据格式解析Rust类型

我们用一个例子来介绍如何手动实现Deserialize trait:

#![allow(unused)]
fn main() {
use serde::de::{self, Deserialize, Deserializer, Visitor, MapAccess};
use std::fmt;

struct Point {
    x: i32,
    y: i32,
}

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("a struct Point with fields x and y")
            }

            fn visit_map<M>(self, mut map: M) -> Result<Point, M::Error>
            where
                M: MapAccess<'de>,
            {
                let mut x = None;
                let mut y = None;

                while let Some(key) = map.next_key::<String>()? {
                    match key.as_str() {
                        "x" => x = Some(map.next_value()?),
                        "y" => y = Some(map.next_value()?),
                        _ => {}
                    }
                }

                let x = x.ok_or_else(|| de::Error::missing_field("x"))?;
                let y = y.ok_or_else(|| de::Error::missing_field("y"))?;

                Ok(Point { x, y })
            }
        }

        deserializer.deserialize_struct("Point", &["x", "y"], PointVisitor)
    }
}
}
  • PointVisitor结构体用于解析JSON字段
  • visit_map解析xy的值,确保字段存在

使用#[derive(Serialize, Deserialize)]标注自动实现SerializeDeserialize

手动实现SerializeDeserialize非常繁琐,通常我们会使用serde_derive宏:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 10, y: 20 };

    let serialized = serde_json::to_string(&p).unwrap();
    println!("Serialized: {}", serialized); // {"x":10,"y":20}

    let deserialized: Point = serde_json::from_str(&serialized).unwrap();
    println!("Deserialized: {:?}", deserialized); // Point { x: 10, y: 20 }
}

使用了SerializeDeserialize的例子

use serde::{Serialize, Deserialize};
use serde_json;

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

fn main() {
    let user = User {
        name: "Alice".to_string(),
        age: 30,
    };

    // 序列化
    let json_str = serde_json::to_string(&user).unwrap();
    println!("Serialized JSON: {}", json_str);

    // 反序列化
    let deserialized: User = serde_json::from_str(&json_str).unwrap();
    println!("Deserialized: {:?}", deserialized);
}

输出:

Serialized JSON: {"name":"Alice","age":30}
Deserialized: User { name: "Alice", age: 30 }

其他注意事项

serde下的serde_derive crate提供了机制,可以覆盖单个字段或枚举变体的序列化。由于serde是第三方库,你可能不希望强制添加对它的依赖。

大多数库都选择了提供了serde功能(feature),只有当用户选择启用该功能时才添加对serde的支持。

也就是说,你要在你的crate里这么写:

[dependencies]
serde = { version = "1.0", optional = true }

[features]
serde = ["dep:serde"]
  • [dependencies]这一块引入了serde,除了写了语义版本之外,还有optional = true,这代表着是一个可选的依赖。这意味着默认情况下不包含 serde,除非显式启用它。
  • [features]这一块写了serde = ["dep:serde"],意思是当用户启用serde feature时,才会启用 serde 依赖
  • "dep:serde"这部分表示依赖 serde 这个库dep:是依赖的前缀,告诉Cargo这个feature依赖serde)

别人要用你的crate(假设my_crate是你的crate的名字)时这么写就可以启用serde:

[dependencies]
my_crate = { version = "0.1", features = ["serde"] }

2.3.2. 不建议实现Copy trait

用户通常不期望类型实现Copy trait。如果用户想要副本的话,通常会调用clone方法。

Copy trait改变了移动给定类型值的语义(Copy trait的语义与实现要求详见 1.9.2. 如何实现Copy trait)。看个例子:

#[derive(Debug,Copy, Clone)]  
struct Point {  
    x: i32,  
    y: i32,  
}  
  
fn main() {  
    let point1 = Point { x: 10, y: 10 };  
    let point2 = point1;  
      
    println!("{:?}", point1);  
    println!("{:?}", point2);  
}

point1的值赋给了point2。一般来说point1至此之后就会失效,但是由于Point结构体实现了Copy trait,所以point1的值赋给了point2发生的是复制而不是移动,point1至此之后仍然保持有效。这点会让用户感到惊讶,所以不建议实现Copy trait。

除此以外,实现Copy的类型会有很多限制,一个最初简单的类型很容易变得不再满足Copy trait的要求。例如持有String或其他不支持Copy trait的类型都不得不移除Copy trait。