[HN Gopher] The Serde Rust Framework
       ___________________________________________________________________
        
       The Serde Rust Framework
        
       Author : ur-whale
       Score  : 90 points
       Date   : 2021-10-14 20:56 UTC (2 hours ago)
        
 (HTM) web link (serde.rs)
 (TXT) w3m dump (serde.rs)
        
       | elktea wrote:
       | Serde is great. I wish it did XML as well but that's a trickier
       | proposition
        
       | pornel wrote:
       | Because Serde is a common API for pretty much all serialisation
       | formats supported in Rust, it's very easy to try multiple formats
       | to choose the best size/speed you need. JSON too big? You can
       | make it CBOR or Msgpack with a couple of lines. Want faster? Call
       | bincode instead.
        
       | AaronO wrote:
       | Whilst not classical de/serialization I wrote serde_v8
       | (https://github.com/denoland/serde_v8), an expressive and
       | ~maximally efficient bijection between v8 & rust.
       | 
       | It has powered Deno's op-layer since 1.9
       | (https://deno.com/blog/v1.9#faster-calls-into-rust-with-serde...)
       | and has enabled significant improvements in opcall overhead
       | (close to 100x) whilst also simplifying said op-layer.
        
         | apendleton wrote:
         | Honestly using serde for language interop is one of my favorite
         | things about serde, whether it's "classical de/serialization"
         | or not. I've recently had the very-pleasant experience of
         | writing some code that needs to pass geospatial data back and
         | forth between Python and Rust, and found that the geojson
         | crate, even though it's nominally for JSON, actually works with
         | other serde-compatible things, including (something I found
         | kind of miraculous) Python objects, using the pythonize crate,
         | which can walk them with serde visitors. So as long as I can
         | get my data into a roughly-geojson-shaped thing on the python
         | side, I can consume it on the Rust side, without having to ever
         | actually produce json.
        
       | roca wrote:
       | serde is one of the best things about Rust in practice.
       | 
       | It is more convenient to use serde to serialize/deserialize to
       | some standard format like JSON or YAML than it is to write your
       | own half-baked format and serialization/deserialization code ---
       | even for the simplest tasks --- so you just stop doing the
       | latter. This is a fundamental shift, and very good for your
       | software.
       | 
       | "Convenience" here actually covers a lot of ground. The APIs are
       | convenient. Boilerplate code is minimal (#[derive(Serialize)]).
       | serde's attributes give you lots of control over the
       | serialization/deserialization, while still being convenient to
       | use. cargo makes importing serde into your project effortless.
       | All of these are necessary to achieve that shift away from custom
       | formats.
       | 
       | Before Rust I used to write a lot of one-off half-baked formats.
       | Pernosco uses Rust and serde and it uses no half-baked formats.
       | Everything's JSON, or YAML if it needs to be more human-editable,
       | or bincode if it needs to be fast and compact and not human-
       | readable or extensible.
        
         | vvanders wrote:
         | It's also blazing-fast.
         | 
         | I've done some benchmarking on JSON -> in-memory borrow structs
         | and it was pretty darn impressive while being fairly ergonomic
         | to use compared to a streaming parser.
        
         | the_duke wrote:
         | The only downside is compile time bloat.
         | 
         | Serde generates heaps and heaps of generic code. This all gets
         | optimized away to be very efficient, but only once it reaches
         | LLVM.
         | 
         | Ever tried working on a crate with hundreds or thousands of
         | de/serializable types? Compile times shoot through the roof
         | really quickly.
         | 
         | The maintainer of serde also created `miniserde` [1] to tackle
         | this problem, which uses dynamic dispatch instead of generics
         | and can have 4x compile time improvements.
         | 
         | Due to Rusts lack of orphan instances you really depend on a
         | pervasive standard for serialization which is used by all
         | libraries though, so the ecosystem is pretty locked in to serde
         | by now.
         | 
         | [1] https://github.com/dtolnay/miniserde
        
         | UK-Al05 wrote:
         | I think in most ecosystems it's easier to use a prebuilt
         | serializer/deserializer than your own format.
        
           | roca wrote:
           | Fair enough. I have not found that to be true in C/C++, which
           | is my main point of comparison. For very simple cases it's
           | generally easier to write a few lines to a text file, maybe
           | with some whitespace separators in the lines, than to use an
           | "real" format.
           | 
           | For JS and Python, JSON.stringify/parse and json.loads/dumps
           | are a step up, but you still end up with an untyped mess with
           | no schema validation, which makes them only halfway solutions
           | to me. I'm a static typing guy at heart, sue me.
        
             | eptcyka wrote:
             | Yea, but with serde, you don't get an untyped mess. You
             | either get the type you expected or you get an error.
        
           | dexwiz wrote:
           | Yeah I don't know why you would try to write your own
           | textbased format. GSON and Jackson have been around for over
           | a decade in Java, and equivalents in other languages. Unless
           | you are in some perf critical application, a library is
           | almost always better for this problem.
        
           | pornel wrote:
           | The trick with Serde is that it decodes straight into a
           | native Rust struct. You get most of validation for free (it
           | also nicely takes advantage of Rust enums with data), and
           | struct access is maximally fast.
           | 
           | A generic JSON decoder would give you a dynamic structure
           | that can contain anything, and then you'd have to pick it
           | apart.
        
             | UK-Al05 wrote:
             | Most deserializers in typed languages allow you to
             | deserialize straight into structs or typed objects...
             | 
             | Serde just doesn't use reflection to do it.
        
               | Taywee wrote:
               | C++ isn't super easy about it, though. The best C++ json
               | library (in my opinion), nlohmann's json, still requires
               | you to define to_json and from_json for your types to use
               | them. It's not too bad as a one-off, but when you have
               | dozens of types, it gets really tedious compared to
               | Serde, and managing std::variant with it is way more
               | annoying than Serde with Rust enums.
        
         | danobi wrote:
         | Completely agree. FWIW, I think procedural macros make a huge
         | difference for ergonomics. For example, a "similar" library in
         | C++ (libcereal) is still a PITA to use and debug.
        
       | xet7 wrote:
       | Is there any Rust web framework that does not have problem with
       | Slow-Loris ?
       | 
       | https://github.com/SergioBenitez/Rocket/issues/1405
        
         | dbrgn wrote:
         | How is that related to serde?
        
         | pornel wrote:
         | Because of the way Rust's async works, you can put a timeout on
         | anything (e.g. you don't need your http parser support
         | timeouts, you can kill it at any await point). So it's only a
         | matter of choosing timeout policy for your app. One person's
         | DoS attack is another person's long polling API.
         | 
         | Anyway, it has nothing to do with Serde, which doesn't have a
         | network component. For Serde you'd typically buffer the input
         | first, or use external framing (like line-oriented JSON).
        
           | the_duke wrote:
           | None of the current Rust async frameworks will prevent your
           | synchronous code from going haywire.
           | 
           | Tokio or async-std timeouts only cancel futures. The
           | dedicated spawn_blocking threadpools also do not support
           | cancellation.
           | 
           | To prevent such cases you would have to spawn the work into a
           | dedicated thread pool and then kill the thread on timeout.
           | 
           | Terminating threads is a very complicated topic though and
           | not generally possible, especially without introducing memory
           | unsafety.
           | 
           | The only real workaround would be to have the synchronous
           | code regularly check for cancellation via an atomic bool or
           | similar, and terminate if required.
        
         | eptcyka wrote:
         | I think this should be fixed in hyper.
        
         | ttymck wrote:
         | This is interesting, and news to me. Your comment suggests
         | something about Rust imposes this problem on all web
         | frameworks, is that true? If so could you share the root cause?
        
           | xet7 wrote:
           | I'm just newbie in Rust. Does some web framework not use
           | hyper? I'm trying to find web framework that does not have
           | problem with Slow-Loris.
        
             | Ericson2314 wrote:
             | I don't think anyone will want to avoid hyper, tbh, that's
             | a lot of work to hand-roll instead. so you better advertise
             | the issue there instead, and if they refuse to do anything
             | about it _then_ make a big stink downstream.
        
       | yazaddaruvala wrote:
       | Frankly Serde's continued success has made it basically necessary
       | for most Rust development. When the Bazzar organically builds and
       | loves a mini-Cathedral it's probably time to upgrade it to
       | standard.
       | 
       | The increased complie times are the only real issue with Serde.
       | 
       | Especially if upgrading this crate into std would allow for a way
       | to reduce the compile time "penalty" of very common formats like
       | JSON, etc.
        
       | paulgb wrote:
       | To me, Serde is one of the killer features of Rust. Once you
       | realize how much of programming is (for better or worse)
       | shuttling data between different representations, it's hard for
       | me to program in a language where I don't have a tool like Serde.
       | 
       | A big part of its flexibility is how modular it is. Most common
       | Rust libraries support serde serialization (at least as an
       | optional feature), so if you use crates that do, you can plug any
       | backend in and serialize those data structures to it. It doesn't
       | even have to be string-based; I've been using Serde to store
       | arbitrary data structures as objects on Google Cloud Firestore.
        
       | [deleted]
        
       | the_gipsy wrote:
       | I just came from rust to go and... what a disappointment.
       | 
       | Missing fields default to some kind of default "zero value" - for
       | any type, even full blown structs. You can't tell the difference
       | of a missing field or the field having the default value.
       | 
       | So if a field has a validation of "must be greater than zero",
       | you can't really give a proper error message. If user puts in "0"
       | or omits the field, you always get a value of "0".
        
         | X6S1x6Okd1st wrote:
         | I've usually seen this handled by having everything be a
         | pointer, the default value of the pointer is nil. You can also
         | use the SQL values.
        
           | the_gipsy wrote:
           | Isn't it insane to change your types to accommodate this? You
           | have to change all fields accesses, it behaves differently,
           | you lose non-nullability...
        
         | apendleton wrote:
         | If you want a field to be able to be not-set, you just need to
         | specify that by making it an `Option<_>` type. So like, if the
         | field is a number, make it an `Option<u64>` -- that way you can
         | distinguish between not-set (`None`) and set to zero
         | (`Some(0)`).
         | 
         | Edit: whoops, misread which language it was you were frustrated
         | with. Nevermind!
        
           | tylerhou wrote:
           | I know you're giving helpful advice, but the author is
           | talking (complaining) about Golang, which has no option
           | typing (unless you want to use a pointer).
        
           | the_duke wrote:
           | You misunderstood, parent is complaining about Go, not about
           | Rust. In Go you would have to use a pointer to represent
           | Option<T>, which makes code really awkward.
           | 
           | Go serialization really has quite a few issues apart from
           | default initialization. Configuration by somewhat weird
           | struct tag strings which are only evaluated/validated at
           | runtime, de/serialization is all done via reflection (unless
           | you want to use code generation), ...
        
       ___________________________________________________________________
       (page generated 2021-10-14 23:00 UTC)