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>, }
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 { 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 { type Ok = (); type Error = Error; 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
| 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
暂时不支持,因为浮点数的反序列化比较麻烦,没计划做,所以序列化时就直接拒绝。
| 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
增加一下可读性。
| 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()) }
fn serialize_str(self, v: &str) -> Result<()> { self.serialize_bytes(v.as_bytes()) }
fn serialize_bytes(self, v: &[u8]) -> Result<()> { self.append_element(v); Ok(()) }
fn serialize_unit(self) -> Result<()> { self.output.extend_from_slice(b"$-1\r\n"); Ok(()) }
fn serialize_none(self) -> Result<()> { self.serialize_unit() }
fn serialize_some<T>(self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(self) }
|
集合类型 - seq
一个列表型的数据,默认第一个元素为“命令”,剩下的元素为“参数”
因此,serialize_seq
会用 传入的长度参数,构建array的开头,*
号引导
| 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 { type Ok = (); type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) }
fn end(self) -> Result<()> { Ok(()) } }
|
相同的逻辑对tuple适用,序列化方法沿用serialize_seq
,SerializeTuple
的实现和SerializeSeq
相同
| fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> { self.serialize_seq(Some(len)) }
|
集合类型 - struct
对于结构体struct,其名称就是”命令”,字段就是“参数”。总共有四种struct,依次说。
最简单的struct类型,unit_struct
,形如struct Foo;
,可以看成无参数命令,所以先写入array开头,长度为1,然后写入命令名称。
| 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开头,写入命令名称,再把唯一的参数序列化。
| 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
直接写。
| 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,直接调用Serialize
trait就完事儿了。
| 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}
,因为我们不关心字段名,比如key
、val
,只在意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的处理。并且使用到的SerializeTupleVariant
、SerializeStructVariant
trait 实现也没有区别
| 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类型的无序性,无法保证序列化顺序,因此并不支持这样的结构
| 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
中没有专门的接口,直接套用bytes
,str
的方案不太行。最开始的想法是自定义Serialize
接口,用一些怪手段来添加-
,比如调用serialize_struct
时,传入的name
,就是添加-
之后的identifier。但是又注意到,这其实这和struct、enum的序列化为array的基本方向冲突。重新看resp中对错误类型的描述,定位好像就是String
,没有什么结构信息,所以更粗暴的做法,可能将错误类型display之后,用serialize_str
去序列化。那更粗暴的,display的工作都可以不交给serde,在构建给客户端的Response时,match一下,如果是error,display,添加-
,然后直接序列化这个字符串。这个流程还可以用宏来简化,比自定义Serialize
就简单多了。