3 ms·
Don't get discouraged by this. The features being added may sound academic and obscure, but they make the language simpler to use. This pattern has already bee
by pornel 5y ago
Don't get discouraged by this. The features being added may sound academic and obscure, but they make the language simpler to use.
This pattern has already been repeated a few times:
Before: "Uuugh, I want to do X, but the compiler says I can't! Why can't I do this simple thing!??"
After: It works.
For example, Rust used to have an embarrassing problem with cloning arrays larger than 32 elements, for silly obscure reasons. Since then it has added a "const generics" feature, and cloning and iteration over arrays works just as you'd expect it to always work.
The current issue is that traits (which are a bit like interfaces in Java) don't quite support async functions, for silly obscure reasons. Currently if you want to have an interface that is both abstract and async, you need to manually implement workarounds that add complexity and overhead. Rust is removing this problem by adding language-level plumbing that was preventing it from having traits just work for any type of function.
Rust's evolution isn't adding bells and whistles, but filling in holes.
- scoutt 5y agoThis is the perfect example. For instance, I don't need Traits or Async or generics for embedded. I can pass a struct into functions as I've been doing my whole life. Async is not supported (but I can generate my own interrupt-driven state machines). In C I don't have an "abstract and async traits interface". I wouldn't even imagine why I need one "in the language". Do I need one? Does a bluetooth stack or kernel driver need one? I can make a struct with pointers to functions, and there you have an "interface", done with the basic elements the language provides. I undertand that these features are a "nice-to-have plus", but what all these updates actually generate in a system/embedded developer like me is fatigue, and not only at cognitive level but also "I've changed compiler version and I have to revalidate all over again". Why? some purple-haired 25 years old across the ocean needed an "abstract and async traits interface". Now the bread on my family table depends on what they've changed. I imagine that perhaps Rust tries to make everyone happy, but here are my two cents from my PoV.
- pornel 5y agoRust doesn't cater to small (short) programs, which some embedded programs may be. It's been originally envisioned for making a modern browser engine, which is almost an entire OS these days, so many of its features start making sense only when you need to work with programs big enough that you can't fit them all in your head at once, and have to maintain for a long time, cooperate with other developers, deal with 3rd party code, etc. Features that are layers of abstraction are hard to explain on their own, so I don't think I can justify them in just a HN comment. I remember when I have been reading about Encapsulation in OOP for the first time, and I completely didn't get it — why would I hide struct fields from myself!? I know they're there! Rust's traits are such an abstract feature that doesn't directly translate to things that CPU does, but it's a building block for a few higher-level language features: • Traits can be used like a Base Class or Interface in OOP, so for example you can make structs "Serializable" and have a data format writer know how to dump them to disk (assuming you need something smarter than fwrite(&struct) due to pointers or portability). Instead of having a one-off function for serializing your particular state, you can make each struct describe itself and "just work" when combined with a JSON/Msgpack/binary serializer. Rust makes good use of this for https://serde.rs https://serde.rs that is pretty nice to use and surprisingly efficient. • Traits can be used with static dispatch and combined with other types at compile time ("Generics" in Rust jargon). It makes them replace many things you'd do with horrible C macro hacks, and you get usually nicer syntax and type checking. For example, you can use containers (hash maps, btree maps) without void* cost and casts, and without include macro hacks. Linear search and linked lists can get you far, but when you need a better container, it's really nice to have best-in-class one available, and as fast as if you handcrafted it yourself. • If you prefer the struct-of-function-pointers approach, Traits can do dynamic dispatch that too ("dyn Trait" in Rust jargon). • Traits are nice to coordinate APIs across libraries. For example in C if you want to stream a file from a network, decompress it on the fly, and decode JSON, you need to write a bunch of glue code, because every library handles buffers slightly differently. In Rust there's io::Read trait, and people agreed to use it, so `json_decode(ungzip(request(addr)))` works, and it's streaming (functions here don't return malloced char*, they return sockets/handles that implement a trait that describes how to get more bytes from them). Sort of like a file descriptor, but using general-purpose feature, and it can also be integrated at compile time, so everything is as inlined and optimized as bespoke code.