{"id":754,"date":"2017-05-12T15:33:58","date_gmt":"2017-05-12T15:33:58","guid":{"rendered":"http:\/\/dlang.org\/blog\/?p=754"},"modified":"2021-10-08T11:11:24","modified_gmt":"2021-10-08T11:11:24","slug":"serialization-in-d","status":"publish","type":"post","link":"https:\/\/dlang.org\/blog\/2017\/05\/12\/serialization-in-d\/","title":{"rendered":"Serialization in D"},"content":{"rendered":"<p><em>Vladimir Panteleev has spent over a decade using and contributing to D. He is the creator and maintainer of <a href=\"https:\/\/github.com\/CyberShadow\/DFeed\">DFeed<\/a>, the software powering the D forums, has made numerous contributions to <a href=\"https:\/\/github.com\/dlang\/Phobos\">Phobos<\/a>, <a href=\"https:\/\/github.com\/dlang\/druntime\">DRuntime<\/a>, <a href=\"https:\/\/github.com\/dlang\/dmd\">DMD<\/a>, and <a href=\"https:\/\/github.com\/dlang\/dlang.org\">the D website<\/a>, and has created several tools useful for maintaining D software (like <a href=\"https:\/\/github.com\/CyberShadow\/Digger\">Digger<\/a> and <a href=\"https:\/\/github.com\/CyberShadow\/DustMite\">Dustmite<\/a>).<\/em><\/p>\n<hr \/>\n<p><img loading=\"lazy\" class=\"alignleft size-full wp-image-181\" src=\"http:\/\/dlang.org\/blog\/wp-content\/uploads\/2016\/08\/d6.png\" alt=\"\" width=\"200\" height=\"200\" \/>A few days ago, I saw this blog post by Justin Turpin on the front page of Hacker News:<\/p>\n<p><a href=\"https:\/\/compileandrun.com\/stuggles-with-rust.html\">The Grass is Always Greener &#8211; My Struggles with Rust<\/a><\/p>\n<p>This was an interesting coincidence in that it occurred during <a href=\"http:\/\/dconf.org\/2017\/\">DConf<\/a>, where I had mentioned serialization in D a few times during my talk. Naturally, I was curious to see how D stands up to this challenge.<\/p>\n<h3>The Task<\/h3>\n<p>Justin&#8217;s blog starts off with the following Python code:<\/p>\n<pre class=\"prettyprint lang-python\">import configparser\r\nconfig = ConfigParser()\r\nconfig.read(\"config.conf\")<\/pre>\n<p>This is actually very similar to a pattern I use in many of my D programs. For example, <a href=\"https:\/\/github.com\/CyberShadow\/DFeed\">DFeed<\/a> (the software behind <a href=\"https:\/\/forum.dlang.org\/\">forum.dlang.org<\/a>), has <a href=\"https:\/\/github.com\/CyberShadow\/DFeed\/blob\/5a2fc9284f4b201be27db6cfca8750c9f9e66fbc\/web.d#L4027-L4042\">this code<\/a> for configuring its built-in web server:<\/p>\n<pre class=\"prettyprint lang-javascript\">struct ListenConfig\r\n{\r\n    string addr;\r\n    ushort port = 80;\r\n}\r\n\r\nstruct Config\r\n{\r\n    ListenConfig listen;\r\n    string staticDomain = null;\r\n    bool indexable = false;\r\n}\r\nconst Config config;\r\n\r\nimport ae.utils.sini;\r\nshared static this() { config = loadIni!Config(\"config\/web.ini\"); }<\/pre>\n<p>This is certainly more code than the Python example, but that&#8217;s only the case because I declare the configuration as a D type. The <code>loadIni<\/code> function then accepts the type as a template parameter and returns an instance of it. The strong typing makes it easier to catch typos and other mistakes in the configuration &#8211; an unknown field or a non-numeric value where a number is expected will immediately result in an error.<\/p>\n<p>On the last line, the configuration is saved to a global by a static constructor (<code>shared<\/code> indicates it runs once during program initialization, instead of once per thread). Even though <code>loadIni<\/code>&#8216;s return type is mutable, D allows the implicit conversion to <code>const<\/code> because, as it occurs in a static constructor, it is treated as an initialization.<\/p>\n<h3>Traits<\/h3>\n<p>The Rust code from Justin&#8217;s blog is as follows:<\/p>\n<pre class=\"prettyprint lang-rust\">#[macro_use]\r\nextern crate serde_derive;\r\nextern crate toml;\r\n\r\n#[derive(Deserialize)]\r\nstruct MyConfiguration {\r\n  jenkins_host: String,\r\n  jenkins_username: String,\r\n  jenkins_token: String\r\n}\r\n\r\nfn gimme_config(some_filename: &amp;str) -&gt; MyConfiguration {\r\n  let mut file = File::open(some_filename).unwrap();\r\n  let mut s = String::new();\r\n  file.read_to_string(&amp;mut s).unwrap();\r\n  let my_config: MyConfiguration = toml::from_str(s).unwrap();\r\n  my_config\r\n}<\/pre>\n<p>The first thing that jumps out to me is that the <code>MyConfiguration<\/code> struct is annotated with <code>#[derive(Deserialize)]<\/code>. It doesn&#8217;t seem optional, either &#8211; quoting Justin:<\/p>\n<blockquote><p>This was something that actually really discouraged me upon learning, but you cannot implement a trait for an object that you did not also create. That&#8217;s a significant limitation, and I thought that one of the main reason Rust decided to go with Traits and Structs instead of standard classes and inheritance was for this very reason. This limitation is also relevant when you&#8217;re trying to serialize and deserialize objects for external crates, like a MySQL row.<\/p><\/blockquote>\n<p>D allows introspecting the fields and methods of any type at compile-time, so serializing third-party types is not an issue. For example (and I&#8217;ll borrow <a href=\"http:\/\/thecybershadow.net\/d\/dconf2017\/#\/6\">a slide from my DConf talk<\/a>), deserializing one struct field from JSON looks something like this:<\/p>\n<pre class=\"prettyprint lang-d\">string jsonField = parseJsonString(s);\r\nenforce(s.skipOver(\":\"), \": expected\");\r\n\r\nbool found;\r\nforeach (i, ref field; v.tupleof)\r\n{\r\n    enum name = __traits(identifier, v.tupleof[i]);\r\n    if (name == jsonField)\r\n    {\r\n        field = jsonParse!(typeof(field))(s);\r\n        found = true;\r\n        break;\r\n    }\r\n}\r\nenforce(found, \"Unknown field \" ~ jsonField);<\/pre>\n<p>Because the <code>foreach<\/code> aggregate is a tuple (<code>v.tupleof<\/code> is a tuple of <code>v<\/code>&#8216;s fields), the loop will be unrolled at compile time. Then, all that&#8217;s left to do is compare each struct field with the field name we got from the JSON stream and, if it matches, read it in. This is a minimal example that can be improved e.g. <a href=\"http:\/\/thecybershadow.net\/d\/dconf2017\/#\/7\">by replacing the <code>if<\/code> statements with a <code>switch<\/code><\/a>, which allows the compiler to optimize the string comparisons to hash lookups.<\/p>\n<p>That&#8217;s not to say D lacks means for adding functionality to existing types. Although D does not have struct inheritance like C++ or struct traits like Rust, it does have:<\/p>\n<ul>\n<li><a href=\"https:\/\/dlang.org\/spec\/class.html#alias-this\"><code>alias this<\/code><\/a>, which makes wrapping types trivial;<\/li>\n<li><a href=\"https:\/\/dlang.org\/spec\/operatoroverloading.html#dispatch\"><code>opDispatch<\/code><\/a>, allowing flexible customization of forwarding;<\/li>\n<li><a href=\"https:\/\/dlang.org\/spec\/template-mixin.html\">template mixins<\/a>, which allow easily injecting functionality into your types;<\/li>\n<li>finally, there is of course classic OOP inheritance if you use classes.<\/li>\n<\/ul>\n<h3>Ad-lib and Error Handling<\/h3>\n<p>It doesn&#8217;t always make sense to deserialize to a concrete type, such as when we only know or care about a small part of the schema. D&#8217;s standard JSON module, <code>std.json<\/code>, currently only allows deserializing to a tree of variant-like types (essentially a DOM). For example:<\/p>\n<pre class=\"prettyprint lang-d\">auto config = readText(\"config.json\").parseJSON;\r\nstring jenkinsServer = config[\"jenkins_server\"].str;<\/pre>\n<p>The code above is the D equivalent of the code <a href=\"https:\/\/news.ycombinator.com\/item?id=14285368\">erickt posted on Hacker News<\/a>:<\/p>\n<pre class=\"prettyprint lang-rust\">let config: Value = serde::from_reader(file)\r\n    .expect(\"config has invalid json\");\r\n\r\nlet jenkins_server = config.get(\"jenkins_server\")\r\n    .expect(\"jenkins_server key not in config\")\r\n    .as_str()\r\n    .expect(\"jenkins_server key is not a string\");<\/pre>\n<p>As D generally uses exceptions for error handling, the checks that must be done explicitly in the Rust example are taken care of by the JSON library.<\/p>\n<h3>Final thoughts<\/h3>\n<p>In the discussion thread for Justin&#8217;s post, Reddit user SilverWingedSeraph <a href=\"https:\/\/www.reddit.com\/r\/programming\/comments\/69necf\/the_grass_is_always_greener_my_struggles_with_rust\/dh7wis7\/\">writes<\/a>:<\/p>\n<blockquote><p>You&#8217;re comparing a systems language to a scripting language. Things are harder in systems programming because you have more control over, in this case, the memory representation of data. This means there is more friction because you <strong>have<\/strong> to specify that information.<\/p><\/blockquote>\n<p>This struck me as a false dichotomy. There is no reason why a programming language which has the necessary traits to be classifiable as a system programming language can not also provide the convenience of scripting languages to the extent that it makes sense to do so. For example, D provides type inference and variant types for when you don&#8217;t care about strong typing, and garbage collection for when you don&#8217;t care about object lifetime, but also provides the tools to get down to the bare metal in the parts of the code where performance matters.<\/p>\n<p>For my personal projects, I&#8217;ve greatly enjoyed D&#8217;s capability of allowing rapidly prototyping a design, then optimizing the performance-critical parts as needed without having to use a different language to do so.<\/p>\n<h3>See also<\/h3>\n<ul>\n<li><a href=\"http:\/\/code.dlang.org\/search?q=serialization\">Serialization libraries on the D package registry<\/a><\/li>\n<li><a href=\"https:\/\/wiki.dlang.org\/Dynamic_typing\">Dynamic Typing in D<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Vladimir Panteleev has spent over a decade using and contributing to D. He is the creator and maintainer of DFeed, the software powering the D forums, has made numerous contributions to Phobos, DRuntime, DMD, and the D website, and has created several tools useful for maintaining D software (like Digger and Dustmite). A few days [&hellip;]<\/p>\n","protected":false},"author":19,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[26,8,9],"tags":[],"_links":{"self":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/754"}],"collection":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/users\/19"}],"replies":[{"embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/comments?post=754"}],"version-history":[{"count":6,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/754\/revisions"}],"predecessor-version":[{"id":760,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/754\/revisions\/760"}],"wp:attachment":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/media?parent=754"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/categories?post=754"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/tags?post=754"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}