serde你在干什么 - 实践篇 - serialize

serde 源码解析 | 用Redis Simple Protocol做练习 Serialize篇

基础结构和接口

根据之前假定的to_bytes接口,现在来填充基本内容。首先需要一个结构体来实现Serializer trait,干脆名字也叫Serializer,拥有一个output字段,这就是写作文的作文本。to_bytes内部,初始化一个serializer,交给目标类型的serialize带领,参观目标类型,参观回来,直接把写好的作文返回。

在简化版的实现中,序列化结果中只有array和bulk string两种类型,所以为serializer提供一个添加bulk string的快捷方式,append_elemet,接受u8序列,统计长度后统一添加length和分隔符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pub struct Serializer {
output: Vec<u8>,
}

// Redis Simple Protocol规定,发往服务端的信息,是bulk string,这里用bytes来表示
pub fn to_bytes<T>(value: &T) -> Result<Vec<u8>>
where
T: Serialize,
{
let mut serializer = Serializer { output: vec![] };
value.serialize(&mut serializer)?;
Ok(serializer.output)
}

impl Serializer {
// Serializer添加bulk String的函数
fn append_element(&mut self, element: &[u8]) {
self.output
.extend_from_slice(&format!("${}\r\n", element.len()).as_bytes());
self.output.extend_from_slice(element);
self.output.push(b'\r');
self.output.push(b'\n');
}
}

具体实现Serializer trait时,是为&mut Serializer实现,因为serializer被以可变借用的形式交给Serialize接口。

一些associated type设置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
impl<'a> ser::Serializer for &'a mut Serializer {
// 直接往output里塞东西,一般设置为()就可以
type Ok = ();
// 序列化过程中发生的错误,因为我们是写入Vec<u8>,出错可能性很小,这里暂时不做过多考虑,
// 放到反序列过程是再提
type Error = Error;

// 支持有状态序列化的一些结构,但是当前实现不需要额外维护一些信息,所以直接沿用Serializer结构
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Self;

}

实现Serializer trait

primitive type

先从简单的入手,对于数值基本类型,全部格式化为String后as_bytes,然后作为以项bulk string插入结果。低精度把实现委托给高精度的实现。这种模式适用于i8, i16, i32, i64, u8, u16, u32, u64

1
2
3
4
5
6
7
8
fn serialize_i8(self, v: i8) -> Result<()> {
self.serialize_i64(i64::from(v))
}

fn serialize_i64(self, v: i64) -> Result<()> {
self.append_element(&v.to_string().as_bytes());
Ok(())
}

f32, f64暂时不支持,因为浮点数的反序列化比较麻烦,没计划做,所以序列化时就直接拒绝。

1
2
3
4
5
6
7
fn serialize_f32(self, _v: f32) -> Result<()> {
Err(Error::Message("float is not supported".to_owned()))
}

fn serialize_f64(self, _v: f64) -> Result<()> {
Err(Error::Message("float is not supported".to_owned()))
}

bool类型本来应该用整形来表示,但是依然已经简化为全员bulk string,就用true/false增加一下可读性。

1
2
3
4
fn serialize_bool(self, v: bool) -> Result<()> {
self.append_element(if v { b"true" } else { b"false" });
Ok(())
}

下面一些其他基本类型的实现,说明都很短,就直接在注释中写了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 单个字符也被当做字符串
fn serialize_char(self, v: char) -> Result<()> {
self.serialize_str(&v.to_string())
}

// 直接把字符串转为bytes,没有转义
fn serialize_str(self, v: &str) -> Result<()> {
self.serialize_bytes(v.as_bytes())
}

// bytes当作列表元素
fn serialize_bytes(self, v: &[u8]) -> Result<()> {
self.append_element(v);
Ok(())
}

// 空值(), bulk string $-1\r\n表示
fn serialize_unit(self) -> Result<()> {
self.output.extend_from_slice(b"$-1\r\n");
Ok(())
}

// None 等效于 ()
fn serialize_none(self) -> Result<()> {
self.serialize_unit()
}

// Some(value) 在Serialize实现中会直接告诉serializer value,
// 所以Some(())在resp中会和None一样,被序列化为$-1\r\n,无法准确表达信息。
// 但是如果客户端、服务端都用同一套数据结构,就可以这一公认的额外信息进行准确推断了。
fn serialize_some<T>(self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}

集合类型 - seq

一个列表型的数据,默认第一个元素为“命令”,剩下的元素为“参数”

因此,serialize_seq会用 传入的长度参数,构建array的开头,*号引导

1
2
3
4
5
6
7
8
9
10
11
12
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
match _len {
None => Err(Error::Message(
"length of sequence can't be determined".to_owned(),
)),
Some(l) => {
self.output
.extend_from_slice(format!("*{}\r\n", l).as_bytes());
Ok(self)
}
}
}

构建完成后,把Self返回,因为在associated type一节,确定了会为Serializer实现SerializeSeq trait。实现非常简单,每次serialize_element就是直接把元素序列化,自然会追加到array中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
impl<'a> ser::SerializeSeq for &'a mut Serializer {
// 和 the serializer 的Ok类型一致.
type Ok = ();
// 和 the serializer 的Error类型一致.
type Error = Error;

fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
// resp 格式,直接往后添加就可以了
value.serialize(&mut **self)
}

// 长度前置,关闭时什么都不用做
fn end(self) -> Result<()> {
Ok(())
}
}

相同的逻辑对tuple适用,序列化方法沿用serialize_seqSerializeTuple的实现和SerializeSeq 相同

1
2
3
4
// tuples和列表基本相同,但是它的长度是确定的
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
self.serialize_seq(Some(len))
}

集合类型 - struct

对于结构体struct,其名称就是”命令”,字段就是“参数”。总共有四种struct,依次说。

最简单的struct类型,unit_struct,形如struct Foo;,可以看成无参数命令,所以先写入array开头,长度为1,然后写入命令名称。

1
2
3
4
fn serialize_unit_struct(self, name: &'static str) -> Result<()> {
self.output.extend_from_slice(b"*1\r\n");
self.serialize_str(name)
}

newtype_struct,形如struct Foo(i32);,单一参数命令,类似地,写入长度为2的array开头,写入命令名称,再把唯一的参数序列化。

1
2
3
4
5
6
7
8
fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.output.extend_from_slice(b"*2\r\n");
self.serialize_str(name)?;
value.serialize(self)
}

tuple_struct,形如struct Foo(i32, i32, i32);,多参数命令,总长度为参数长度+1,这里先调用serialize_seq,启动array的构造,写入array头部,返回的Serialzier,也就是变量tuple,也实现了SerializeTupleStruct,交给Serialize调用,一项项地把参数序列化,但是在交出去之前呢,需要先写入命令名称,所以调用serialize_str直接写。

1
2
3
4
5
6
7
8
9
10
// tuple_struct,多参数命令,array长度是tuple长度+1,比如 struct Foo(i32, i32, i32);
fn serialize_tuple_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct> {
let tuple = self.serialize_seq(Some(len + 1))?;
tuple.serialize_str(name)?;
Ok(tuple)
}

实现SerializeTupleStruct trait 和Seq一样,每一项field,直接调用Serializetrait就完事儿了。

1
2
3
4
5
6
7
8
9
10
11
12
13
impl<'a> ser::SerializeTupleStruct for &'a mut Serializer {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
fn end(self) -> Result<()> {
Ok(())
}
}

最后还有一种常规struct,形如struct Foo {key: i32, val:i32},因为我们不关心字段名,比如keyval,只在意i32具体是哪个数,所以抛弃字段名后(serialize_field时忽略了_key参数),和tuple_struct没什么两样,所以直接利用已有实现就好了。返回的SerializeStruct也是Serializer,无缝衔接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
self.serialize_tuple_struct(_name, len)
}

impl<'a> ser::SerializeStruct for &'a mut Serializer {
type Ok = ();
type Error = Error;

fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}

fn end(self) -> Result<()> {
Ok(())
}
}

集合类型 - enum

然后处理enum类型,实际上也有四种variant,和struct对等,都可以沿用struct的处理。并且使用到的SerializeTupleVariantSerializeStructVariant trait 实现也没有区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn serialize_unit_variant(...) -> Result<()> {
self.serialize_unit_struct(variant)
}

fn serialize_newtype_variant<T: ?Sized + Serialize>(...) -> Result<()> {
self.serialize_newtype_struct(variant, value)
}

fn serialize_tuple_variant(...) -> Result<Self::SerializeTupleVariant> {
self.serialize_tuple_struct(variant, len)
}

fn serialize_struct_variant(...) -> Result<Self::SerializeStructVariant> {
self.serialize_tuple_struct(variant, len)
}

集合类型 - map

整个过程中,都没有实现serialize_map,因为我们向服务器端传递的是命令,存在执行的先后顺序,但是map类型的无序性,无法保证序列化顺序,因此并不支持这样的结构

1
2
3
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
unimplemented!("map type is not supported")
}

总结

这里序列化的前提,是站在客户端的角度,全部序列化为bulk of string。

如果一定要支持自描述的序列化,也就是站在服务端的角度,除了* $的提示符,还要增加- + :的提示,有什么思路呢?基本的struct、enum转变为array的大方向不变,基础类型序列化时,bytes对应$str对应+,整形对应:,都好办,但是这个-怎么添加?rust中的Error就是普通的struct或者enum,data model中没有专门的接口,直接套用bytesstr的方案不太行。最开始的想法是自定义Serialize接口,用一些怪手段来添加-,比如调用serialize_struct时,传入的name,就是添加-之后的identifier。但是又注意到,这其实这和struct、enum的序列化为array的基本方向冲突。重新看resp中对错误类型的描述,定位好像就是String,没有什么结构信息,所以更粗暴的做法,可能将错误类型display之后,用serialize_str去序列化。那更粗暴的,display的工作都可以不交给serde,在构建给客户端的Response时,match一下,如果是error,display,添加-,然后直接序列化这个字符串。这个流程还可以用宏来简化,比自定义Serialize就简单多了。