[HN Gopher] A Rust API Inspired by Python, Powered by Serde
       ___________________________________________________________________
        
       A Rust API Inspired by Python, Powered by Serde
        
       Author : lukastyrychtr
       Score  : 51 points
       Date   : 2025-05-11 16:28 UTC (4 days ago)
        
 (HTM) web link (ohadravid.github.io)
 (TXT) w3m dump (ohadravid.github.io)
        
       | ohr wrote:
       | (Author here) I needed to do a bit of "reflection" in a Rust
       | crate but didn't want to implement a procedural macro, so I used
       | Serde (which is a (de)serialization crate) instead.
       | 
       | This is also a deep dive into Serde internals - hope you'll like
       | it!
        
       | dundarious wrote:
       | Pardon me, but I prefer the original by 1 million miles.
       | let res = raw_api::query("SELECT * FROM Win32_Fan");       for
       | obj in res {         if obj.get_attr("ActiveCooling") ==
       | Value::Bool(true) {             if let Value::String(name) =
       | obj.get_attr("Name") {                 if let Value::UI8(speed) =
       | obj.get_attr("DesiredSpeed") {                     println!("Fan
       | `{name}` is running at {speed} RPM");                 }
       | }         }       }
       | 
       | If actually concerned about the need to know UI8, then create a
       | typedef DesiredSpeedT or similar. This is equivalent to the
       | struct Fan.
       | 
       | Edit: I understand the post is probably more of a playful
       | exercise than anything else, but I really think the original is
       | far far better (smaller, simpler, etc.) and hope that is not lost
       | on people.
        
         | ohr wrote:
         | That's understandable, but I think it depends on how many
         | different structs like this you have and how many fields you
         | need to work with (for our usecase, we had tens of structs with
         | tens of fields each).
         | 
         | There's also an Alternatives section in the article about other
         | approaches that can achieve similar results, but of course 'do
         | nothing' is also a valid option.
         | 
         | Edit: > If actually concerned about the need to know UI8 ..
         | 
         | Just a small note: even if you don't care about the fact that
         | it's a UI8, you still have to use the correct type. For
         | example, if the field happens to be returned as UI4, this code
         | won't work!
        
           | dundarious wrote:
           | Right, but isn't the struct definition equivalent in line
           | count and effort compared to some typedefs and perhaps a
           | handful of trivial-to-inspect oneline helper functions?
           | 
           | Regarding the UI8, don't you have to get your version's
           | struct data member type correct to the exact same degree as a
           | typedef in my suggestion?
        
             | ohr wrote:
             | > don't you have to get your version's struct data member
             | type correct
             | 
             | No, since Serde will happyly fill a `u64` field with any
             | other `u{8,16,32}` value, and even with signed types (as
             | long as the actual value is non-negative) - this is sort of
             | what happens when you deserialize a JSON `[1, 2, 3]` into
             | `[u64]`.
        
               | dundarious wrote:
               | Yes, but an equivalent to `impl<'de> Deserializer<'de>
               | for ValueDeserializer` handles that. That could be a
               | useful helper.
        
         | LtWorf wrote:
         | I wrote typedload in python. Once they show you an API with
         | hundreds of types you appreciate not having to do like that all
         | the time.
        
           | dundarious wrote:
           | I don't see the issue with just using an equivalent to
           | `impl<'de> Deserializer<'de> for ValueDeserializer` then.
        
             | LtWorf wrote:
             | There's unions, there's stuff that uses reserved words in
             | the language as field names... You are obviously not
             | familiar with this task.
        
         | olalonde wrote:
         | Why? It's much more verbose and error prone (e.g. "stringly
         | typed"). Do you never deserialize JSON?
        
           | dundarious wrote:
           | What's the difference between mistyping in the string here
           | and mistyping in the struct definition? And yes I have.
        
             | olalonde wrote:
             | You only need to get it right once, and from then on the
             | compiler will catch any mistakes if you use it incorrectly.
             | In contrast, every time you write
             | obj.get_attr("DesiredSpeed"), there's a chance you'll make
             | a typo and the compiler won't warn you about it.
        
       | dgacmu wrote:
       | This kind of sells the reason not to wrap things behind an object
       | interface, doesn't it?                   for fan in
       | c.query("SELECT * FROM Win32_Fan"):             if
       | fan.wmi_property("ActiveCooling").value is True:
       | print(f"Fan `{fan.wmi_property('Name').value}` is running at
       | {fan.wmi_property('DesiredSpeed').value} RPM")
       | 
       | vs "SELECT Name, DesiredSpeed from Win32_Fan where ActiveCooling"
       | 
       | Obviously, this doesn't matter when you have 5 fans, but in
       | general, you want to push your restrictions as deeply into the
       | query as possible from an optimization standpoint.
        
         | ohr wrote:
         | In WMI, the fields are lazy loaded when you do a `*` query, but
         | the real crate [does use the same Serde reflection
         | tricks](https://github.com/ohadravid/wmi-
         | rs/blob/main/src/query.rs#L...) to create the correct field
         | list when you query a struct which improves perf a lot!
        
         | vlovich123 wrote:
         | > Obviously, this doesn't matter when you have 5 fans, but in
         | general, you want to push your restrictions as deeply into the
         | query as possible from an optimization standpoint.
         | 
         | Depends where the database lives. If it's an in-process SQLite
         | DB instance, there's no difference & doing this in code is
         | easier to understand than more complicated SQL queries (of
         | course not necessarily in this case but in general). But in all
         | other cases you are correct about efficiency in general
         | (although again other effects can dominate & make it
         | irrelevant).
        
       | lnyng wrote:
       | Interesting post. We wrote this "below" utility [1] that monitor
       | system metrics similar to atop. We want the ability to collect
       | all metrics into a single object, pass it around and visualize it
       | elsewhere. Naturally we need some way to query into fields or
       | even nested-struct fields. For example, to get the file cache
       | usage of a particular process, we need to go through
       | sample->processes->pid->memory->file cache. To do it
       | ergonomically and also type-safely, we end up using proc macro to
       | generate enums that represent field paths of the structs and then
       | use them to query values of non-struct (leaf) fields. I always
       | wonder if there are simpler ways or existing proc macro derives
       | to safe us the efforts. Maybe I do need to look into serde
       | internals for some inspirations.
       | 
       | [1]
       | https://github.com/facebookincubator/below/blob/main/below/b...
        
       | lovasoa wrote:
       | In my opinion, the clean way to implement this is with methods
       | instead of attributes for name, desired_speed, etc...
        
       | xpe wrote:
       | The title is vague in my opinion. What kind of API? What problem
       | does it hope to solve? The article uses querying system data as
       | examples, but after skimming it, I'm not sure why I would care.
       | My comment is also a criticism of the article, since I couldn't
       | skim in quickly to figure out if I should spend more time on it.
        
         | ohr wrote:
         | (Author here) Thanks! That's useful feedback.
         | 
         | I also agree - the final article isn't skim-friendly enough,
         | which drives away some readers.
        
       | vlovich123 wrote:
       | I don't really understand what this offers above diesel.rs which
       | AFAIK is a similar reflection interface except with much more
       | flushed out ORM capabilities (much more complex filtering,
       | joining etc) & support for an assortment of SQL dialects.
        
       | VWWHFSfQ wrote:
       | > let res: Vec<Fan> = query();
       | 
       | It might feel more natural, and less magical if this used a
       | turbofish instead                   let res = query::<Fan>();
       | 
       | Very neat
        
       ___________________________________________________________________
       (page generated 2025-05-15 23:01 UTC)