【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 methodsfn 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 序列化反序列化。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Flask】四、flask连接并操作数据库

目录 前言 一、 安装必要的库 二、配置数据库连接 三、定义模型 四、操作数据库 1.添加用户 2.删除用户 3.更新用户信息 4查询所有用户 五、测试结果 前言 在Flask框架中&#xff0c;数据库的操作是一个核心功能&#xff0c;它允许开发者与后端数据库进行交互&#xf…

手机实时提取SIM卡打电话的信令声音-新的篇章(三、Android虚拟声卡探索)

手机实时提取SIM卡打电话的信令声音-新的篇章(三、Android虚拟声卡探索) 前言 前面的篇章中&#xff0c;我们从理论方向和实际市面上出现的音频线传输声音的方式&#xff0c;讨论绕开手机对SIM卡电话通话声音的封锁场景的可行性&#xff0c;并实际选购几款数字和模拟的USB转接…

安装fpm,解决*.deb=> *.rpm

要从生成 .deb 包转换为 .rpm 包&#xff0c;可以按照以下步骤修改打包脚本 1. 使用 fpm 工具 fpm 是一个强大的跨平台打包工具&#xff0c;可以将 .deb 包重新打包成 .rpm&#xff0c;也可以直接从源文件打包成 .rpm。 安装 fpm sudo apt-get install ruby-dev sudo gem in…

青出于“蓝”的合资第一新能源,“换壳”背后有门道

文/王俣祺 导语&#xff1a;千呼万唤始出来的新能源“马6”终于亮相了&#xff0c;这款马自达EZ-6本以为凭借马自达多年来在国内市场深耕的底蕴可以收获一片支持&#xff0c;但最近却深陷“换壳”风波。那么今天我们就一起看看&#xff0c;这款马自达EZ-6和被冠以“原型”的深蓝…

HT8787B 可任意限幅、内置自适应升压的2x9.0W立体声音频功放

1 特性 ● 可任意配置的限幅功能 自由配置音频限制幅度&#xff0c;使输出音频信号限制在固定失真水平内 ● 内置自动限温控制功能 适应不同散热条件&#xff0c;避免出现过温关断现象 ● 高效自适应G类升压功能&#xff0c;有效延长播放时间 可调节最大限流值&#xff0c;有效…

Ansible基本使用

目录 介绍 安装 inventory-主机清单 分组 子组 modules-模块 command shell script file copy systemd yum get_url yum_repository user mount cron 介绍 ansible是基于python开发的自动化运维工具。架构相对比较简单&#xff0c;仅需通过ssh连接客户机执行…

Java并发常见面试题总结(上)

线程 ⭐️什么是线程和进程? 何为进程? 进程是程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff0c;因此进程是动态的。系统运行一个程序即是一个进程从创建&#xff0c;运行到消亡的过程 在 Java 中&#xff0c;当我们启动 main 函数时其实就是启动了一…

38.第二阶段x86游戏实战2-HOOK窗口消息机制(解决多开窗口句柄问题)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

【原创分享】详述中间件的前世今生

中间件是一种软件组件&#xff0c;位于应用程序和操作系统之间&#xff0c;通过提供统一的接口和功能来简化开发和管理应用程序、提高应用程序的可靠性和性能。 中间件的前世可以追溯到20世纪80年代的分布式系统和网络技术的发展。在那个时候&#xff0c;随着计算机网络的普及…

vite+vue3+ts使用vue3-draggable-resizable,可拖动、调整大小的模块库,真的太好用了!

前言&#xff1a;公司业务&#xff1a;在很多地方装了路灯&#xff0c;给路灯做了个管理系统&#xff0c;有管理系统肯定就是增&#xff0c;删&#xff0c;查&#xff0c;改&#xff0c;但是这次改好像有点复杂。给路灯下发指令&#xff0c;可以在路灯的大屏放文字&#xff0c;…

想给视频去背景音乐?简单几步搞定

在数字视频编辑和处理中&#xff0c;去除视频中的背景音乐是一个常见的需求。无论是因为背景音乐与视频内容不符&#xff0c;还是出于版权考虑&#xff0c;掌握视频去背景音乐的方法都显得尤为重要。那么&#xff0c;视频怎么去背景音乐呢&#xff1f;本文将详细介绍几种去除视…

C语言串口接收GPS数据

要在C语言中接收GPS数据&#xff0c;需要使用串口通信来与GPS设备进行数据交互。一个简单的串口通信代码主要包含了以下几个部分&#xff1a; 1.标准库头文件 stdio.h&#xff1a;包含输入输出函数&#xff0c;如 printf string.h&#xff1a;包含字符串处理函数&#xff0c…

深度学习:yolov3的使用--建立模型

使用argparse模块来定义和解析命令行参数 创建一个ArgumentParser对象 parser argparse.ArgumentParser() 训练的轮数&#xff0c;每批图像的大小&#xff0c;更新模型参数之前累积梯度的次数,模型定义文件的路径。 parser.add_argument("--epochs", typeint, d…

smuge error

0 Preface/Foreword 1 解决方法 第一步&#xff1a;跳过大文件下载&#xff0c;到时候统一使用快速批处理fast batch git lfs install --skip-smudge 故意敲错指令&#xff0c;会出现git lfs install 的usage&#xff1a; 第二步&#xff1a;clone仓库 下载结果&#xff1a;…

R语言机器学习与临床预测模型79--机器学习总览

R小盐准备介绍R语言机器学习与预测模型的学习笔记 你想要的R语言学习资料都在这里&#xff0c; 快来收藏关注【科研私家菜】 01 机器学习分类 机器学习模型主要分为有监督、无监督和强化学习方法。 监督学习 监督学习是教师向学生提供关于他们在考试中是否表现良好的反馈。其中…

黑马软件测试第二篇_功能测试

软件质量模型 应用场景&#xff1a;提供对于软件产品从测试角度思考的一种思路 定义&#xff1a;实际实现的产品和需求描述是否相一致&#xff0c;相一致程度高说明质量满足需求&#xff08;好&#xff09; 如何评判软件的质量&#xff1f; 功能&#xff1a;软件产品是否具备某…

React 中组件通信的几种主要方式

一、父传子&#xff1a; 1. 传递多个属性 父组件可以通过 props 传递多个属性给子组件。 示例 // 子组件 function Son(props) {return (<div>This is Son, {props.name} - Age: {props.age}</div>); }// 父组件 function App() {const name This is App N…

精华帖分享|如何全方位无死角保护你的交易所账号

本文来源于量化小论坛策略分享会板块精华帖&#xff0c;作者为菊座&#xff0c;发布于2024年3月4日。 以下为精华帖正文&#xff1a; 01 背景 起因是这两周保4赚麻了&#xff0c;看到中性群里大佬们的曲线&#xff0c;大家都想加大仓位&#xff0c;于是我在群里提醒了下大家资…

【STM32】SD卡

(一)常用卡的认识 在学习这个内容之前&#xff0c;作为生活小白的我对于SD卡、TF卡、SIM卡毫无了解&#xff0c;晕头转向。 SD卡&#xff1a;Secure Digital Card的英文缩写&#xff0c;直译就是“安全数字卡”。一般用于大一些的电子设备比如&#xff1a;电脑、数码相机、AV…

11月第一篇新作,十一月对我好一点:C++之继承(2)

C之继承&#xff08;2&#xff09; 虚继承 很多⼈说C语法复杂&#xff0c;其实多继承就是⼀个体现。有了多继承&#xff0c;就存在菱形继承&#xff0c;有了菱形继承就有 菱形虚拟继承&#xff0c;底层实现就很复杂&#xff0c;性能也会有⼀些损失&#xff0c;所以最好不要设计…