39 comments

[ 3.2 ms ] story [ 36.8 ms ] thread
Serde is one of Rust's killer feature which makes you appreciate the philosophy behind the language. With just a few annotations you have a robust and complete parser for whatever format (JSON, TOML, etc.) automatically built for serializing and deserializing your data structures. And it's fast, and optionnally zero-copy. I've never seen this in other languages.
Common Lisp?
Have you got a link to the Common Lisp library that lets you annotate data structures so you can use the annotations to parse or dump data by 3rd party libraries for various formats?
No, but given that Common Lisp macro capabilities trump what Rust is capable of, that would be easily achievable.

Or now that I think of it, Template Haskell.

Haskell? Data generics isn't really nothing new. We had this technology for years.
At the same level of performance though?
If you are asking if it's fast? yes it's fast serialization. Not sure which numbers you have at hand, but for me fast means it isn't the bottleneck of my application.
it doesn't have to be to make the point, as Serde isn't on the same level of verbosity as Aeson.
Haskell has been around for more than a decade and had huge uptake by language nerds, but the only significant OSS written in it is Pandoc. In contrast, Rust already has Servo and a number of CLI tools, ripgrep being the most prominent.
I'm not sure what you consider 'significant', but to my mind, there are many great haskell open source projects: xmonad, shellcheck, stack, haxl, to name a few.

Not to mention how many languages are written in it: Elm, Purescript, Idris, GHC itself.

I almost listed xmonad. I haven’t heard of the other ones. Doesn’t change my point.
Leaving popularity contests aside, i worked in a company using Haskell and i did used a lot these techniques, if that counts \o/

i guess today it only matter how many stars you repo has in github...

True. Not widely available though. The great thing about Rust is that has a whole of the advantages of Haskell, and C++, and to a lesser extent even languages like Python.
It is best not to confuse deserialisation and parsing. Parsing is a separate task with separate issues.

For JSON deserialisation, Serde is a very convenient API to use an existing JSON parser that someone else has (hand) written.

If you needed a new syntax, you'd have to write a new parser. I often implement communications protocols as part of my work. In most cases, Serde doesn't help me much there - I have to parse the protocol myself (e.g. using Nom).

What is the difference between parsing and deserializing? With both you have a string representation of a data structure that you read and bring into the native form. Do you call it parsing if the grammar is more complicated?
Serde only helps with moving the values from the parser into a struct. It doesn't help with figuring out the data format.
Deserialisation (in this context) is the act of decoding data from some "string representation" to a representation suitable for use, typically a native format.

Typically, although this depends on the specific encoding, a parser is required as one of the first stages of a deserialiser; normally a parser refers to something which applies the rules of something as least as complex as a CFG, as opposed to a lexer/scanner which typically just applies regular expressions; a combination of the two allows the serialised format to be interpreted.

You can show quite easily that JSON (and indeed any CFL) cannot be "parsed" using a lexer alone.

Good question, thanks.

I had the vague notion that parsing was for human readable content and deserialization was for computer-to-computer content.

Now I tentatively make the distinction that compiling transforms streams into code (stream -> lex -> parse -> magic -> code) whereas deserialization is for data. Making parsing just one of the transformation steps for both compilation and deserialization.

(comment deleted)
Serde includes a bunch of parsers by default but if you invented a new text file format, you’d have to write a new parser for it before you could do anything with it, and serde doesn’t help with that.
The really essential difference is that when you deserialize, you trust the input, and so don't need to check syntax or validity. I.e., you assume it is output of your own serializer, and you can therefore be faster than if you didn't trust it.

I have no idea if serde follows this design, or if it has just hijacked the terms.

D also has serialization libraries based on introspection capabilities. Orange is an example of such library https://code.dlang.org/packages/orange . Here's a blog series (in progress) showing how to write a serializer/deserializer from scratch in D: https://bradley.chatha.dev/Home/Blog?post=JsonSerialiser1
Thanks for sharing. I'm not familiar with D, but it looks like compile time type introspection is something which is built into the language, which removes the need for something like the serde derive macro. Is that correct?
Yes. D metaprogramming is mostly based around template and it gives you compile-time traits which you can use to query structs/classes for their fields, determining if something is an array or a pointer etc.

This way, you can write a serialize(foo) function which will work for any type, without needing any derives or marking the type as Serializable like in Java.

The closest equivalent to derive in D would be a mixin. You could declare a mixin which autogenerates the toJSON() fromJSON() methods based on the fields of a struct.

Serde is very strong indeed (although not the easiest to understand, this would have been useful when I implemented a Serde library for [de]serialising with the AWS DynamoDB AttributeValue format).

That said, I've always had a slight problem with this approach to serialisation in general, which is that it makes it very hard to have multiple serialisation approaches for a single type (e.g. I want one casing rule for one target, and a different one for another). This seems effectively impossible with an approach based on annotations/macros. Would love to know how others have dealt with that.

> I want one casing rule for one target, and a different one for another

Hmm, this is interesting, and something I hadn't thought of before. I'd probably try to solve it with a combination of the newtype pattern (create wrapper types for `MyThing`, like `MyThingJson` and `MyThingToml`) and the Serde flatten attribute. Then you could, for example, add `#[serde(rename_all = "lowercase")]` to the `MyThingJson` struct and `#[serde(rename_all = "snake_case")]` to the `MyThingToml` struct.

I haven't tested this though, so I'm not sure if the `rename_all` would flow through the flatten attribute as expected. Worst case you could duplicate the struct (or more likely, find or build a macro to do it for you) rather than use the newtype/flatten technique.

This is where I would either a) create 2 separate DTO representations with adapters to the internal domain object or b) have a trait which the 2 specialized structs implement. This conversion point is also a good place to do your validation that the object is in a consistent state. This isn't quite zero-cost - but it does isolate your external serialization code from your internal logic and it is easier to add even more formats in the future.

And at least in Java-land is much cleaner than stacking lots and lots of annotations on the same struct.

Pronunciation question: Does Serde have one syllable or two? Looking at the word out of context, it seems the e at the end ought to be silent. But it's derived as an abbreviation from SERialize/DEserialize. Does the author or community have a consensus on one of these being the "correct" form?
I think it has two, although I wouldn't be confident in telling you what they are. I've always pronounced it sair-day (where sair rhymes with hair).
I would say two, I personally pronounce "de" like in DElaware.
It's abserde to not use only one syllable!

... Sorry, just trying to be funny.

I'm not sure about the rust package, but in the hardware world SerDes ("serializer / deserializer") is pronounced "sir-deez."
Original author of serde here. I call it sir-dee, but the current maintainer, dtolnay, calls it sear-day. But we really don't care how people pronounce it. It's really more of a neither / potato thing:

https://www.youtube.com/watch?v=K75g7eRhH9M

Just wanted to thank you for the great work. Thanks!
To me, pronouncing it seer-dee would make sense, because that's how I'd pronounce seer-ialize / dee-serialize. However, for some reason I still pronounce it sir-dee regardless :)