【Rust中的序列化:Serde(一)】

Rust中的序列化:Serde

  • Serde是什么?
  • 什么是序列化序列化?
  • Serde运行机制
    • Serde Data Model
    • Vistor Api
    • Serializer Api
    • Deserializer Api
  • 具体示例流程分析
    • 具体步骤:
    • 那么依次这个结论是如何得出的呢?
    • 什么是'de?
  • 总结

Serde是什么?

Serde is a framework for serializing and deserializing Rust data structures efficiently and generically.
名字是序列化和反序列化的缩写,serde就是一种高效且通用的Rust数据结构序列化反序列化框架。


什么是序列化序列化?

序列化指的是将定义的结构化数据转换成更容易存储和或传输的形式,如字节流。而反序列化则是将如流式数据重新恢复成本来的样子,方便开发者解析和处理逻辑。通常情况下序列化反序列化使用在网络通信上。如我们熟知的protobuf等。

Serde运行机制

如下图:
Serde运行机制

Serde Data Model

The Serde data model is the API by which data structures and data formats interact. You can think of it as Serde’s type system.

Serde 数据模型是DataType(DataStruct)与 DataFormat交互的Api,你可以认为它就是Serde的类型系统,
其中包含了Serialze与DeSerialze的Api,同时也有Vistor的Api,可以说,每一种类型的Api都对应了一批Api函数,每一个Api函数又会对应一种类型。
也就是Serde Data Model 是整个转换过程中的中间环节,DataType和DataFormat之间是不互知的,双方都只需要将各自的数据通过Serde Data Model Api转换成Serde Data类型。

Vistor Api

	fn expecting(&self, formatter: &mut Formatter<'_>) -> Result;
    fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_none<E>(self) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
       where D: Deserializer<'de> { ... }
    fn visit_unit<E>(self) -> Result<Self::Value, E>
       where E: Error { ... }
    fn visit_newtype_struct<D>(
        self,
        deserializer: D,
    ) -> Result<Self::Value, D::Error>
       where D: Deserializer<'de> { ... }
    fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
       where A: SeqAccess<'de> { ... }
    fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
       where A: MapAccess<'de> { ... }
    fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
       where A: EnumAccess<'de> { ... }

Serializer Api

    // Provided methods
    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> { ... }
    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> { ... }
    fn collect_seq<I>(self, iter: I) -> Result<Self::Ok, Self::Error>
       where I: IntoIterator,
             <I as IntoIterator>::Item: Serialize { ... }
    fn collect_map<K, V, I>(self, iter: I) -> Result<Self::Ok, Self::Error>
       where K: Serialize,
             V: Serialize,
             I: IntoIterator<Item = (K, V)> { ... }
    fn collect_str<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
       where T: ?Sized + Display { ... }
    fn is_human_readable(&self) -> bool { ... }
	fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error>;
    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error>;
    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error>;
    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error>;
    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error>;
    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error>;
    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error>;
    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error>;
    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error>;
    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error>;
    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error>;
    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error>;
    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error>;
    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error>;
    fn serialize_none(self) -> Result<Self::Ok, Self::Error>;
    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
       where T: ?Sized + Serialize;
    fn serialize_unit(self) -> Result<Self::Ok, Self::Error>;
    ..

Deserializer Api

fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_byte_buf<V>(
        self,
        visitor: V,
    ) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_unit_struct<V>(
        self,
        name: &'static str,
        visitor: V,
    ) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
    fn deserialize_newtype_struct<V>(
        self,
        name: &'static str,
        visitor: V,
    ) -> Result<V::Value, Self::Error>
       where V: Visitor<'de>;
       ...

具体示例流程分析

具体步骤:

  1. 初始化工程:
cargo init whatserde
  1. 将serde引入cargo.toml
serde = { version = "1", features = ["derive"] }
  1. main.rs
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct MyTestData {
    a: u64,
    b: String,
}
fn main() {
    println!("Hello, world!");
}
  1. 使用cargo expand展开代码
    没有安装cargo-expand的开发者可根据Link说明安装expandLink
cargo expand > expand.rs

执行后将得到一份展开后的代码如下(部分展示):

  1. 序列化代码:
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
    #[allow(unused_extern_crates, clippy::useless_attribute)]
    extern crate serde as _serde;
    #[automatically_derived]
    impl _serde::Serialize for MyTestData {
        fn serialize<__S>(
            &self,
            __serializer: __S,
        ) -> _serde::__private::Result<__S::Ok, __S::Error>
        where
            __S: _serde::Serializer,
        {
            let mut __serde_state = _serde::Serializer::serialize_struct(
                __serializer,
                "MyTestData",
                false as usize + 1 + 1,
            )?;
            _serde::ser::SerializeStruct::serialize_field(
                &mut __serde_state,
                "a",
                &self.a,
            )?;
            _serde::ser::SerializeStruct::serialize_field(
                &mut __serde_state,
                "b",
                &self.b,
            )?;
            _serde::ser::SerializeStruct::end(__serde_state)
        }
    }
};

可以看到,序列化器先去序列化struct在分别支持序列化字段a和b,最后以end结尾,嵌套类的DataType也是如此,层层递进的序列化最后以end为标识符,表示到达结尾。
2)反序列化代码:

            #[doc(hidden)]
            const FIELDS: &'static [&'static str] = &["a", "b"];
            _serde::Deserializer::deserialize_struct(
                __deserializer,
                "MyTestData",
                FIELDS,
                __Visitor {
                    marker: _serde::__private::PhantomData::<MyTestData>,
                    lifetime: _serde::__private::PhantomData,
                },
            )

观察deserialize_struct 是元组结构体,其中的包含__deserializer反序列化器,结构体名称,字段FILEDS,Visitor访问器,其中,
FILEDS:

const FIELDS: &'static [&'static str] = &["a", "b"];

为Visitor提供了访问顺序,visitor便会按照顺序依次访问下面的字段,在通过反序列化器调用对应的反序列化接口将字段解析,直到没有下一个字段。

那么依次这个结论是如何得出的呢?

代码如下:

#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
    #[allow(unused_extern_crates, clippy::useless_attribute)]
    extern crate serde as _serde;
    #[automatically_derived]
    impl<'de> _serde::Deserialize<'de> for MyTestData {
        fn deserialize<__D>(
            __deserializer: __D,
        ) -> _serde::__private::Result<Self, __D::Error>
        where
            __D: _serde::Deserializer<'de>,
        {
            #[allow(non_camel_case_types)]
            #[doc(hidden)]
            enum __Field {
                __field0,
                __field1,
                __ignore,
            }
            #[doc(hidden)]
            struct __FieldVisitor;

可以观察到,有枚举值__filed0,__filed1,__ignore。这侧面印证了serde通过FILEDS顺序来使用__FiledVisitor访问每一个字段并反序列化。
------------------------__ignore是什么?
默认情况下,serde支持序列化方传来的DataType类型有增加(但不能减少),这会大大提高兼容性,(这有点像protobuf中的默认option),反序列化所需要的字段都存在,反序列化就不会出问题。
Serde支持了许多的Attributes,来限制或者扩展:
#[serde(rename = “?”)] 字段重命名。
#[serde(bound = “T : MyTrait”)] 限制只有实现了某种特征才能被序列化反序列化。
#[serde(default)] 即给予字段一个默认值,如果它为空的话。而不是报错。
#[serde(crate= “ …”)],即作为crate引入时可根据此标签重命名依赖包名称和导入。
具体的可以参考这里

什么是’de?

注意到,在反序列化中引入了一个生命周期【'de】,一般情况下,我们常见的生命周期要么是【'static】要么是单字符【`a】

来看看官方给出的解释:

This lifetime is what enables Serde to safely perform efficient zero-copy deserialization across a variety of data formats, something that would be impossible or recklessly unsafe in languages other than Rust.
Zero-copy deserialization means deserializing into a data structure, like the User struct below, that borrows string or byte array data from the string or byte array holding the input. This avoids allocating memory to store a string for each individual field and then copying string data out of the input over to the newly allocated field. Rust guarantees that the input data outlives the period during which the output data structure is in scope, meaning it is impossible to have dangling pointer errors as a result of losing the input data while the output data structure still refers to it.

也就是说,这个因为Rust的生命周期规则,Rust可以安全高效的使用零Copy反序列化方案,而这在其他语言中几乎必然是不安全的。

#[derive(Deserialize)]
struct User<'a> {
    id: u32,
    name: &'a str,
    screen_name: &'a str,
    location: &'a str,
}

Rust保证了在作用于下输入数据的寿命必然输出数据结构的寿命,这意味着在输出结构仍引用它的情况下是不可能出现悬垂指针的,保证了程序的安全和高效。


总结

以上便讨论完毕基本的Serde原理,后续计划会继续讨论如何实现Custom 序列化反序列化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/905958.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于生成式人工智能的工业互联网安全技术与应用研究

摘要&#xff1a;近年来&#xff0c;人工智能技术飞跃式发展&#xff0c;工业互联网安全与大模型、生成式人工智能等新技术融合成为研究的重点方向和难题。针对工业互联网安全领域面临的突出性问题&#xff0c;对该领域人工智能应用现状进行了分析与研究&#xff0c;提出了一种…

神舟十九号发射取得圆满成功 科技自强自力筑梦天穹

10月30日凌晨神舟十九号载人飞船发射取得圆满成功&#xff01; 图片来自网络 当日凌晨4时27分&#xff0c;搭载神舟十九号载人飞船的长征二号F遥十九运载火箭在酒泉卫星发射中心点火发射取得圆满成功。执行神舟十九号载人飞行任务的航天员乘组由蔡旭哲、宋令东、王浩泽3名航天…

PHP反序列化原生类字符串逃逸框架反序列化利用

PHP反序列化 概念 序列化的原因&#xff1a;为了解决开发中数据传输和数据解析的一个情况(类似于要发送一个椅子快递&#xff0c;不可能整个椅子打包发送&#xff0c;这是非常不方便的&#xff0c;所以就要对椅子进行序列化处理&#xff0c;让椅子分成很多部分在一起打包发送…

KINGBASE部署

环境&#xff1a;x86_64 系统&#xff1a;centos7.9 数据库–版本&#xff1a;KingbaseES_V008R006C008B0014_Lin64_install 授权文件–版本&#xff1a;V008R006-license-企业版-90天 一 前置要求 1.1. 硬件环境要求 KingbaseES支持通用X86_64、龙芯、飞腾、鲲鹏等国产C…

建筑行业知识库搭建:好处、方法与注意事项

在建筑行业&#xff0c;知识管理对于提升项目效率、降低成本、增强创新能力以及构建竞争优势具有至关重要的作用。搭建一个高效、系统的建筑行业知识库&#xff0c;不仅有助于实现知识的有效沉淀与便捷共享&#xff0c;还能促进知识在项目实践中的灵活应用&#xff0c;从而加速…

图书管理系统(JDBC)

AdminUser是管理员类 NormalUser是用户类 AddOperation是增加图书类 BorrowOperation是借书类 DelOperation是删除图书类 ExitOperation是退出类 FindOperation是查找图书类 IOPeration是接口 ReturnOperation是还书类 ShowOperation是显示所有图书类 注意&#xff1a…

liunx CentOs7安装MQTT服务器(mosquitto)

查找 mosquitto 软件包 yum list all | grep mosquitto出现以上两个即可进行安装&#xff0c;如果没有出现则需要安装EPEL软件库。 yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm查看 mosquitto 信息 yum info mosquitto安装 mosquitt…

如何保护网站安全

1. 使用 Web 应用防火墙&#xff08;WAF&#xff09; 功能&#xff1a;WAF 可以实时检测和阻止 SQL 注入、跨站脚本&#xff08;XSS&#xff09;、文件包含等常见攻击。它通过分析 HTTP 流量来过滤恶意请求。 推荐&#xff1a;可以使用像 雷池社区版这样的 WAF&#xff0c;它提…

【CPN TOOLS建模学习】设置变迁的属性

使用Tab键在属性之间进行切换 与一个变迁相关联的四个铭文&#xff0c;均为可选项&#xff1a; 变迁名称守卫(Guard)时间代码段 变迁延迟必须是一个正整数表达式。该表达式前面加上&#xff0c;这意味着时间铭文的形式为 delayexpr。在添加时间铭文之前&#xff0c;铭文的默…

Cityscapes数据集:如何将像素级的多边形标注的分割数据标注转为目标检测的bbox标注

Cityscapes数据集官网下载地址&#xff1a; https://www.cityscapes-dataset.com/ 相关介绍&#xff1a;从官网下载这三个压缩包文件leftImg8bit_trainvaltest.zip、gtCoarse.zip、gtFine_trainvaltest.zip 1&#xff09;leftImg8bit_trainvaltest.zip分为train、val以及tes…

NVR批量管理平台EasyNVR多品牌NVR管理工具/设备关于远程控制的详细介绍

在数字化安防时代&#xff0c;视频监控技术已成为维护公共安全、提升管理效能的重要基石。随着科技的飞速发展&#xff0c;流媒体技术和视频监控正经历着前所未有的变革与融合。NVR批量管理平台EasyNVR&#xff0c;作为一款基于“云-边-端”一体化架构的视频融合云平台&#xf…

理解原子变量之一:从互斥锁到原子变量,最粗浅的认识

目录 三个实例对比 结论 多线程编程对于程序员来说&#xff0c;是一项非常重要的技能。在C11标准问世之前&#xff0c;C标准是不支持多线程的。在C11出台前&#xff0c;如果你想在linux平台进行多线程编程&#xff0c;就要使用linux的多线程库pthread&#xff0c;而pthread是…

eBay自养号测评养成攻略:从环境系统搭建到养号下单

eBay账号在测评中的重要性不言而喻&#xff0c;然而&#xff0c;新注册的账号往往面临被封禁或下单即封的风险。如何养成稳定的买家号&#xff0c;成为众多商家关注的焦点。以下将详细讲解eBay测评中如何稳定养成买家号。 一、账号注册后的初期养护 新注册的eBay账号&#xf…

Docker(二):Docker的基本使用

1 Docker的基本使用 1.1 镜像相关操作 1、从DockerHub搜索镜像 [rootmaster ~]# docker search centos # 镜像名字 描述 星标 是否官方&#xff08;有OK表示为官方镜像&#xff09; NAME …

【hector mapping参数设置】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 hector mapping部分参数介绍调整hector_mapping中的参数ros常见问题总结 hector mapping部分参数介绍 在wiki.ros.org/hector_mapping界面找到3.1.4 Parameters章节…

使用wordcloud与jieba库制作词云图

目录 一、WordCloud库 例子&#xff1a; 结果&#xff1a; 二、Jieba库 两个基本方法 jieba.cut() jieba.cut_for_serch() 关键字提取&#xff1a; jieba.analyse包 extract_tags() 一、WordCloud库 词云图&#xff0c;以视觉效果提现关键词&#xff0c;可以过滤文本…

ios Framework版本号的问题。

自己创建的framework和普通的app的版本号设置的地方是有所有不同的。 framework 的版本号是在 TARGETS -> Build Settings -> current Project Version 这个地方设置的&#xff0c; 在创建framework的时候xcode 会自动创建一个framework.h的文件名&#xff0c;framewo…

Axure设计之多级菜单导航教程(中继器)

在数字化时代&#xff0c;优化产品设计&#xff0c;提升用户界面交互&#xff0c;是产品设计着重考虑的点。针对传统菜单导航复杂繁琐的问题&#xff0c;本设计提出了一套灵活的菜单导航方案&#xff0c;结合中继器与动态面板&#xff0c;实现一键搜索、菜单收藏、多级菜单导航…

真题总结和整理

补码的符号位在最高位 IEEE754 规格化要求 小数点前面是1,其他的认为是小数点后面为1即可 计算之前要对阶 左移和右移在寄存器中如果未说明定点数,可以通过移动小数点实现 涉及最小帧长要记得除以2 求用于外设的时钟周期数 指令两端只允许有寄存器,间接寻址要通过MA…

计组-层次化存储结构

这里主要看存储的整体结构&#xff0c;cache&#xff0c;内存 这里看存储结构是按什么样的层次来划分存储结构&#xff0c;速度由慢到快&#xff0c;容量由大到小&#xff0c;这是基于性价比的考虑&#xff0c;所以分为多级多层次&#xff0c;可以做到提高速度的同时没有增加多…