21 comments

[ 2.7 ms ] story [ 54.0 ms ] thread
Such fameous hacker like Zalewski should better distinguish language and library interfaces design issues.
This was my first thought as well. Rust certainly lends itself to unnecessarily complicated API designs, but so does C: OpenSSL requires something like 5-6 layers of indirection to do basic things like converting a sequence of X.509 structures into a buffer of PEMs.

I've yet to discover a language that doesn't allow a sufficiently clever engineer paint themselves into a corner with API complexity. Even deliberately simple languages like Python suffer from this.

Yeah, this is just trolling. There's nothing inherent to rust that requires ( or even recommends) writing code like this. There's nothing inherent to C that prevents bad API designs like this.

Nothing about this post is notable or interesting. It's low-effort rage bait.

"There's nothing inherent to C that prevents bad API designs like this."

I don't have any data to back this and library interface designs are so personal taste-prone, but I would say that the average Rust crate has a better API than the average C library.

Say that to corporate java writers. Language creates culture and this applies to human as much as computer.
So my instinct to avoid Java at all costs wasn't irrational dysphoria after all!
The JavaEE APIs essentially set the example for everybody to then imitate. Of course it didn’t help that design patterns were in peak fashion. The Java core language is small, and most of the SE APIs aren’t particularly byzanthine. But EE then went all in for some reason.
It it what happens when you try to make an universal API for any thinkable and unthinkable use-case.
Your mention of a "universal API" reminded me of the paper "Some Were Meant for C" [1] in which the author argues that one of the most important feature of C is its "communicative design", i.e., its ability to manipulate any memory -- whether this memory originates from the program or is foreign -- with the usual C operators (+, *, [], <<, >>, etc.)

[1] https://www.humprog.org/~stephen//research/papers/kell17some...

(comment deleted)
Yeah, not a big fan of the library ecosystem in embedded rust, but then again I've never been a big fan of the libraries I get off the shelf for the vast majority of embedded C. The rust libraries have the additional complication of trying to keep you from doing bad things with the registers, but then commonly don't expose enough for doing anything beyond example applications.

Either case it's way easier to just twiddle the bits yourself, and can be clearer is that code is tailor made to your use case.

FWIW, I still jump to Rust to begin with, just ignore most libraries.

I don’t know if this one of the embedded Hal libs or not. But what they do is to move the state of GPIO etc into the type system. So the type system maps a state machine. In practice it’s impossible to flip a bit on a pin which is not in the right configuration etc. My understanding was that this is the hot stuff that rust offers to embedded programming. Compiletime error handling. But in reality it can look overly complicated. Again I don’t know if this is a direct example or some other library. I played around with one on my blue pill a couple of years back. And yes the c api brings you faster results.
I'm generally a fan of encoding invariants in the type system, but there's a balance to be had between that and the library becoming relatively unusable. I haven't used embedded Rust very much, but my general impression from afar, and from talking to people who are experts in the domain, is that the ecosystem leans far too much into the type system.
I agree. But the point still is that the post wants to make it look like: this is how rust looks like. Look it is as bad as enterprise Java.
I think from the lens the author is looking at, this is undeniably true. It isn't the complete picture but the folks working on embedded Rust should take it to heart.
C is an abstraction layer for assembler, created at a time when software people were very close to the hardware. Not surprising that it works very well for those use cases.

Recent languages tend to come from the other direction.

C is a high level programming language
This is one of my big frustrations with the embedded rust ecosystem (not a problem with the language IMO). They are so hell-bent on taking the typestate pattern and various abstractions to their extreme to prevent you from ever doing anything bad with the registers that the cognitive overhead of the API is unusable.

It's all great and clean if you're just writing led blinky examples. As soon as you need to dig down to some obscure datasheet minutia (as you invariably have to for any decent size embedded project), it becomes very annoying to map this back to the safe API. The code also becomes less readable since you can no longer look up things you don't understand in the datasheet.

The other this is that because of the excessive use of typestate, any driver struct needs to have a separate generic parameter for each io pin.

To be honest, at Oxide we have basically skipped a lot of the standard Embedded Rust stack. We don't use embedded-hal, and don't really like the style of code svd2rust spits out.

We haven't produced an alternative, so are still using those lower-level svd2rust APIs. But the pain points are very real.

Overall, still quite happy with embedded Rust overall, dealing with annoying APIs is just part of doing professional work.

(Hubris' design also mitigates this issue indirectly, since you're communicating with a driver, where this kind of code lives, so most of the code has a much nicer API, which is also why it's not the end of the world.)

A subset of Ansi C works on a 4 MHZ homebrew CPU made of TTL's.