[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)