These are all real issues with Rust, though it's worth noting that many of the integration challenges mentioned also apply to external code written in C and C++.
Some of the survey responses highlight one of the biggest hurdles to rust adoption I've experienced though:
Rust has an education problem. People dramatically overestimate the correctness of their [C/C++] code and underestimate the potential severity of failures to meet that expectation. I've found very few projects where a bit of poking can't turn up memory safety issues. Even people who are far more skilled programmers than I am routinely write silly mistakes.
Unfortunately the response I often hear to bringing these issues up after the code exists is "it's working code, why should I care about these theoretical issues?"
The C and C++ ecosystems are suffering from a selection effect, in that the people that have any concern about their code being correct are trying to drop them, while the people comfortable with the language are all the ones that don't know about the problems.
I expect the careless unaware culture to get worse with time as more and more aware people manage to leave it.
>the people comfortable with the language are all the ones that don't know about the problems.
Or the ones writing code where security doesn't matter, like HFT and video games (the former have no users, and the latter are basically impossible to make crack-resistant even if they're written entirely in Rust).
Others have corrected you about gaming; I would point out that for HFT if you write a memory error, you are reasonably likely to encounter it because of the sheer number of swings of the stick you get at hitting the bug. And if that code's concurrent at all, and it probably is, it had even moreso better get it right. You do not want to discover memory errors at run time.
I'd bet HFT has already got all the best static analysis money can buy, not that they're cowboying it more than anybody else.
Memory safety isn't just about "security". Sure, that's a huge issue, but it's also about not spending a month debugging why the login fails one out of a thousand logins because the user's password gets replaced by what seems to be a pointer, but one that doesn't point at anything, starting with the 5th character, or why your HFT code should have ordered 10,000 shares but instead ordered a pointer's worth of shares, or why the second level crashes after precisely the 1023rd frame with completely incomprehensible errors, or any number of other things.
I do not know why any sane developer that has worked through such a thing even once would be willing to entertain the notion of programming without it if it weren't utterly forced on them. I enjoy reading such tales of diagnostic heroism; I don't enjoy being in them anywhere near as much as some programmers apparently do.
> Or the ones writing code where security doesn't matter, like HFT and video games
A lot of games have a multiplayer mode nowadays, that opens it up for exploits. I do not want to get my PC hacked because I connected to a game server. If you mean only single player offline games, I guess yes security is less of an issue.
Is video game security an entire subfield of its own? I imagine there are categories of exploits in video games which simply don’t exist in other areas of software
The latest in FPS cheating (that I'm aware of, not like I'm super plugged into the underground) involves buying a second PC to run the cheats, a card for your main PC to grab a copy of memory over DMA, ship it off to the second PC, then joining the two video feeds together. Apparently you can also hook your mouse up to a connection where it will edit the data flowing from the mouse to give you better aim as well.
A lot of it is the same as any other sort of security stuff, but like, the tough part is that the adversary has access to the physical machine. In my understanding anyway, not a security expert.
The "end-game" for cheating software is just a camera pointed at the monitor, or an HDMI splitter. Run the video feed through a not-invented-yet AI/ML model, and the model will control your character via valid USB keyboard/mouse inputs.
"Behavioral" anti-cheat will have to finally become a thing. As a game designer in 5-10 years, you won't be able to assume the operating system will be privy to any data which indicates cheating is happening.
I believe the right answer would be to cluster players by behavior signatures, leading to all the bots being in clusters with eachother. Then bots can mostly just play other bots. Over time you can merge known "human" clusters together so that different play styles get to play in the same matches. This also has the benefit of clustering "toxic" players together - both by chat content and in-game player behavior.
You would only ever do all that if you had no clue what you are doing (and most don't). It's rather trivial to bypass modern kernel anticheats, especially with a hacked up KVM or custom hypervisor. So much of anti-cheat nowadays is based off of automated (delayed) detection and user reports that you can easily ragehack to the the top ranks without ban if you just use silentaim (aimbot that doesn't need to change your view angles) and common sense.
In fact, I'd go so far as to say that a majority of players in the top ranks of most popular FPSs are cheaters or queue with cheaters. There's nothing quite like watching the enemy stare right at you through every wall across the map and then carelessly run in a straight line towards you. Even more concerning is how many streamers queue up with blatant cheaters and then use their connections with game moderators to manually ban other cheaters.
Security != DRM, i.e. 'anti-cheat'. Security is more about 'a malicious player can't RCE other players' than "cheaters can't access data which is sent to their PC anyway". One is a lot more tractable than the other.
Yes. Basically cheats and anti-cheats became their own thing security-wise and huge amounts of effort are spent (by both sides) on this cat and mouse game.
Although both cracking (as in "software cracking") and cheats were very similar security fields back in the day (both boiled down to reverse engineering) cheating has diverged enough (due to modern anti-cheats and anti-piracy becoming very different countermeasures) that I'd consider them vastly different nowadays.
Some categories of exploits unique to games:
- Aim hacking (pointing the mouse cursor to enemy heads)
- Recoil/spread cheats (mouse compensating for weapon recoil or bullet spread)
- Botting/botfarming (playing resource-intensive games automatically)
- Wallhacking (showing players through walls, or making walls semi transparent)
- Miscellaneous passive assistance (like overlaying a predicted path for a ball in a game, drawing precise location for footsteps/other player sounds, etc.)
- Modification of game state (like sending your character's position at will, allowing you to fly or go through walls)
Some of these have analogues in app security:
- Botting detection is probably very similar in both MMOs and CloudFlare)
- Hidden information is not very different from what we do in web apps (only send the state that the client needs and has access to)
- Preventing game state modification is solved by having a strongly authoritative server (again pretty normal in app land)
But even in those, what makes games very unique compared to other apps is that they're hard real time: you have hard requirements for each frame time; it's expensive to calculate in-server all things that a player should be able to see every single frame; packets take time to travel over the internet so you have to give it some slack (or enemies could pop out of nowhere when crossing doors); client sometimes needs information that you'd like to remain private (you need player positions even behind walls to provide 3D sounds)...
Modern anticheats usually resort to just scanning the memory for running cheats, trying to detect a cheat reading/modifying the game memory, etc. but cheats have come to great lengths like having actual cheat hardware (DMA devices on PCIe[0]) that cannot be detected since it's running outside of the computer software.
First, 100% of HFT are businesses trying to make money above all else. Security and correctness are secondary to the business requirement of making money, especially when you are uniquely situated to have financially quantified their risk when things go wrong to remain within controlled error bands.
Second, HFT developers are using very expensive FGPAs to do trading with network packets. People are quick to reach to some example where their favorite toy is used at some organization, but that's besides the point. The tools for getting code on those FPGAs is horrible and unless Xilinx adopts Rust internally I doubt much will change. Rust can be adopted elsewhere, but that's not what's making the organization money. HFT folks are not persuaded even slightly by these arguments about security and correctness. Jane Street is not a HFT firm and they will gladly tell you that.
It helps immensely to understand ones concerns, constraints, and values before assuming they're identical and them swiftly demonizing anyone that dissents. The Rust community really has done itself a massive disservice in alienating everyone that doesn't conform to its monoculture composed mostly of amateurs and unemployable extremists.
Of course, this will simply be downvoted by the pathetic and unimpressive. Nobody views this as an opportunity to even try disputing the argument or use it as a legitimate data point that Rust should strive for. They simply want obedience and mindlessness in the monoculture because they lack the tools needed to handle conflict of any kind. It's also why so many features are perpetually stuck as nightly rather than stable. The project does nothing but stultify talent into leaving for genuine innovation.
> It helps immensely to understand ones concerns, constraints, and values before assuming they're identical and them swiftly demonizing anyone that dissents. The Rust community really has done itself a massive disservice in alienating everyone that doesn't conform to its monoculture composed mostly of amateurs and unemployable extremists.
I think that it's a little unlikely that the Rust community is mostly composed of "amateurs and unemployable extremists" - a lot of the working software engineers I know are interested in or actively use it.
However, "assuming they're identical and them swiftly demonizing anyone that dissents" is an excellent characterization of, at least, the parts of the Rust community that I see on HN and Reddit.
> People dramatically overestimate the correctness of their [C/C++] code
Genuinely interested: what do people think of their tons of third-party dependencies in Rust? My experience building Rust projects is that there are orders of magnitudes more dependencies than what would make me comfortable. At least in C/C++ it's much harder to get there.
I use embedded Rust for my day job. We keep our third-party dependencies purposefully minimal, but I adore that they're there when I need to reach for them. Like, we just needed a basic TCP stack to get our PoC up and running, so I can just grab smoltcp and be off to the races. Being written in C or C++ wouldn't obviate our need for a TCP stack, I'd still either need to get it from somewhere else or write it myself. And even if I can't use a third-party dependency for whatever reason (e.g. if it doesn't support no_std mode), I can still usually repurpose their existing test suite to give me confidence in my port or reimplementation.
I'm somewhat puzzled - the obvious answer is that you don't have to use them. Rust by itself is no less feature complete than C. Anything you can write in C without dependencies, you can write in Rust without dependencies too.
Of course, dependency-reliance falls on a spectrum for any individual programmer. On the one hand you have script kiddies with 400 npm dependencies gathered from arcane Discord channels - on the other you have old bearded wizards who don't understand why you'd need printf() when you can just mov rax 1; syscall.
I find Rust is not strongly opinionated about this. You can just as easily eschew dependencies as use them. There's usually a good array of options available, or you could always FFI to a C lib directly if you need to.
Because you find it fine to say that "people dramatically overestimate the correctness of their [C/C++] code", but not fine to say that "people tend to depend on too many third-parties that they never question"?
We have solid data on how much Rust improves safety, while “too many” dependencies seems rather arbitrary. They tend to be high quality and better vetted/tested than bespoke code or copy-pasted hpp headers
> They tend to be high quality and better vetted/tested
I thought anyone could publish rust packages, just like for PyPi? Are the packages audited before they are published in Rust?
> “too many” dependencies seems rather arbitrary
I would say that you have "too many" dependencies when you don't have control over them, e.g. because you can't afford auditing them or maintaining them. I have seen multiple Rust projects that I would rather rewrite myself than audit their tens (hundreds) of dependencies. That's "too many".
> I thought anyone could publish rust packages, just like for PyPi? Are the packages audited before they are published in Rust?
Who prevents nasty C/C++ packages from being published? To my knowledge, anyone can just publish a C/C++ library.
> I would say that you have "too many" dependencies when you don't have control over them, e.g. because you can't afford auditing them or maintaining them. I have seen multiple Rust projects that I would rather rewrite myself than audit their tens (hundreds) of dependencies. That's "too many".
Then don't use them?
Your concerns appear to boil down to 'bad software exists'. Which... yeah. It does. Not sure what you want us to say. It's an intrinsic characteristic of open source / free software, and it's not like C/C++ is somehow under-represented (or Rust over-represented) in dodgy software.
There's plenty of closed ecosystems you can stick to if you don't trust yourself not to use dodgy dependencies (though I think this is a very strange concern to have).
> Who prevents nasty C/C++ packages from being published? To my knowledge, anyone can just publish a C/C++ library.
Distros have system package managers that are checked by the distro maintainers. And by definition you trust the maintainers of your distro.
Somehow it feels like Rust does not go really well with distros (because it pushes for static linking).
> Your concerns appear to boil down to 'bad software exists'.
I was reacting to someone generalizing on C/C++ code, saying that "people dramatically overestimate it". I feel like it's nice to have some memory safety brought by Rust, but I find it a bit hypocritical to criticize the security of C/C++ code if Rust projects tend to statically link random code from hundreds of dependencies nobody ever reads. Because that's a big security issue IMO.
Sure, you can do it right with Rust. But you can also write good code with C/C++.
> Distros have system package managers that are checked by the distro maintainers. And by definition you trust the maintainers of your distro.
You're comparing apples to oranges. Most C/C++ software in business use isn't pulling its dependencies from distro PMs, it's pulling them from some monstrous dark web of Visual Studio projects and opaque git repos. And on the other hand, Rust software that's distributed in distro PMs uses distro dependencies like any other package. Equally, as you note, the reverse exists. None of this tells us very much about Rust or C/C++ as languages.
> Rust projects tend to statically link random code from hundreds of dependencies nobody ever reads
'Tend to' is doing a lot of heavy lifting in that sentence, and doesn't seem to be based on very much more than 'I saw it a few times'. Well, I've seen my share of nightmarish C++ dependency trees. Is that a good argument against C++? Not really.
You're picking up on real issues - unnecessary and untrusted dependencies - except you're then erroneously drawing some imagined line in the sand where Rust bad and C/C++ good, which is where a rational argument seems to collapse into vibes. On the whole, there's a lot more C++ projects out there guilty of this than Rust projects.
> Sure, you can do it right with Rust. But you can also write good code with C/C++.
You can hammer a nail with a broom, but that doesn't make it the best tool for the job. The safety advantages of Rust can't just be hand-waved away. C is a fine programming language when safety is less of an issue. If you're writing a Nintendo emulator, sure, C, why not. If you're writing business logic that handles health data, maybe not.
> Rust software that's distributed in distro PMs uses distro dependencies like any other package.
I haven't found a single Rust package that depends on a shared Rust library coming from the system package manager. Do you have an example of that? Genuinely interested in seeing how it looks. Many people tell me that Rust "doesn't support shared libraries", which seems odd to me. But I have never seen one, so...
> except you're then erroneously drawing some imagined line in the sand where Rust bad and C/C++ good
I don't think that, maybe I did not express myself correctly. I was answering to a comment that, to me, sounded like "C/C++ bad and Rust good", by saying that I also see issues in Rust (namely with the dependency management).
I like Rust-the-language. What I don't like, to be honest, is how Rust people seem to believe that static linking is the solution to all their problems.
Last I checked Rust itself does not have a stable ABI, so there are effectively no "shared Rust libraries" outside of static linking / compiling from source.
But they can and do simply use C's ABI when dynamic linking, because that is stable. Rust-to-Rust libraries generally don't bother with this because Cargo exists and that trivializes "build from source" and is far more configurable and optimizable and compile-time-provable, but it's used any time Rust uses C-compatible stuff or vise versa and that's quite common.
I am not reading anything saying that there exists C/C++ code. I read code and make my own opinion.
Most code is bad, but that's true for Rust as well. The memory safety does not magically make code good. BTW Rust is not the only language that has memory safety, by far.
Huh? Now I'm quite puzzled. I find both fine to say. I think I make it pretty clear in my comment that dependency use is a per-programmer issue. If you come to Rust with an unquenchable dependency-lust, then yeah, you'll wind up with a Cargo file that looks like the Chicago phonebook. But - here's the kicker - you can do that with C or C++ too! There's really nothing about Rust that requires or recommends this, is my point.
Rust is a generally safer language than C, this much is fairly well established. That alone is an intrinsic advantage. I'm the sort of dev to inline handwritten assembly for a performance boost, so I feel very warmly towards C (which is just assembly in a wig), but for those things where correctness and safety is more important, Rust is simply the better tool.
> Genuinely interested: what do people think of their tons of third-party dependencies in Rust?
Here are three things I think, and they have in fact nothing to do with Rust:
1. The easier it is to add dependencies, the more dependencies will be added on average - unless you work purposefully against that.
2. The effect of a rising average number of dependencies in libraries is that their number of dependencies grows as well, and the number of their dependencies' dependencies... up to dependency graphs of several hundred nodes size. Like in exponential growth. An example would be the dependency graph of jquery.
3. I observe this "exponential" growth can have chain-reaction-like effects, like if you have a mass of U235 that achieves critical mass. Below that critical value, some neutrons flying around might trigger a few fissions, but these die out. Above that value, neutrons lead to fissions which lead to more neutrons and so on. The same can happen with complexity in multi-component software. At some point, complexity goes through the roof.
And the latter is especially true if backwards compatibility is not strictly observed, since backwards-incompatible changes tend to be infectuous in that they often make their client components (parents in the dependency graph) backwards-incompatible as well, in other words, there is breakage that propagates up the dependency graph. That breakage might die out and be able to be contained by local fixes, or it might propagate. And once your dependency graph becomes large enough, it is almost guaranteed, that you have breakage.
All these things together is why I believe that systems like NixOS or Guix are the future (but of course there might be other developments in that space).
> All these things together is why I believe that systems like NixOS or Guix are the future (but of course there might be other developments in that space).
That, or actually keeping control over the dependencies?
I think it is always wise to constrain unneeded dependencies. And this counts even more in embedded systems.
But on the other hand, programming artefacts, languages and their library systems compete in terms of features, which will usually lead to an ever growing number of dependencies. At least this is what we observe. Even if not all of this is really necessary, this would probably be hard to reverse in general.
> At least in C/C++ it's much harder to get there.
Interesting take. Poor package management as a security feature.
As others have pointed out, instead of potentially poorly written third-party dependencies C/C++ will just have potentially poorly written homebrew replacements.
> Interesting take. Poor package management as a security feature.
How poor is it to install a package with your system package manager instead of the language package manager? I wouldn't call it poor.
The biggest difference I see is that language package managers are usually not curated (and that's a feature: "it's much faster than getting your package accepted in a distro").
My preference is to rely on curated package managers when I can. When I can't, then I am the maintainer, and therefore I need to handle the dependencies myself. You can call it "poor", but the fact that I maintain my dependencies is actually a security feature. When you depend on 600 third parties that you don't even know and that are not curated, it is a security problem.
Isn't this hierarchical trust? You select immediate dependencies that you believe are written by reasonably security-conscious people, not just anything that immediately solves a problem, and because they're reasonably security-conscious people they select their own dependencies the same way? And at each level vetting is implicitly shared with other users of the dependencies, some of whom will be more critical/inquisitive.
I've heard the alarms about dependencies and I'm not sold. I feel like this is bleeding over from the JS/frontend world where people don't choose to do the above, for whatever reason.
Whenever I add a dependency in Rust I look at crates.io downloads, dependents (any big projects there?), github stars, I browse the issues to see what sort of problems have been reported and when, with what sort of replies from the author, how many contributors there are, release history, what commits look like, and what other stuff the authors have worked on. I use a lot of dependencies, and I do rewrite stuff myself when I feel like I can't rely on a dependency.
That's the thing about trust: not everyone uses it the same. There are documented examples of malware being injected to millions of dependents through package managers, so we know that "hierarchical trust" does not work.
The big difference with a distro is that I choose to trust the distro maintainers, which is only a handful of people. Whereas with your hierarchical trust, you choose to trust many random people.
I know I remember there being a couple big events in the news, and I know the Javascript ecosystem is also a bigger target also because it's so popular, but do you have examples of these attacks?
> People dramatically overestimate the correctness of their [C/C++] code and underestimate the potential severity of failures to meet that expectation
Really depends on the field. I heard more than once in my life now that it's better to reboot every night than spend even an afternoon of engineering trying to fix memory leaks.
That's good when it's just about memory leaks. Your missile guidance code can have leaks as long as the missile completes the mission, since it will all be... "garbage collected" anyway.
It's not great when you have problems like use after free and pointers going to where they don't belong. Those can cause security vulnerabilities that rebooting won't fix.
So you write a note mentioning there's a leak, and debug it if there's a need for it.
Finding a bug doesn't mean you need to fix it, or fix it right away. Using Rust doesn't preclude resource leaks. I'm pretty sure I've managed to run into resource leaks in all of the languages I've used in production, doesn't matter if they were managed or not. Sometimes limiting the lifetime of the thing that holds resources, and let process (or system) death clean it up works great; sometimes that's terrible, depends on the cost of death/restart, the lifetime between deaths, human intervention required, accuracy of lifetime estimation method, probably some other things. I don't worry too much about leaks that require a restart every year or so; nor would I worry about a leak in a missile that will cause issues beyond the achievable maximum flight time.
Linux supports overcommitted memory and so will lie to your application about memory availability. When I tested several common open-source applications in a memory-constrained environment none of them, except for the Java VM, handled OOM conditions gracefully. I came to the conclusion it's not worth the programming effort unless your application will specifically expect this condition to occur.
I work on systems that don't run on Linux, and those systems also have reasonable business requirements for graceful failure on OOM. This is unhelpful.
That seems completely reasonable. The only interesting case is "There was far too little storage, so we gave up early" e.g. you need 14GB, there is 186MB available, give up. I probably don't want my photo viewer to crash because I tried to open a JPEG that wouldn't fit in RAM, better it just says that's too big and I can pick a different file.
Whenever people are imagining they're going to recover despite OOM in low level components they are tripping. Their recovery strategy is going to blow up too, and then they're going to be in a huge mess - just abort when this happens.
Firefox and Chrome (and thus Edge, Brave, OperaGX, etc etc) do the same for many allocations - it's safer to crash than to end up in an obscure failure path that never had its error handling exercised and may accidentally be security sensitive.
I had to read this a couple of times, but I suspect this is about non-rust code, when you cite correctness of code, yes? People don't want to use rust because they think their janky C/C++ is just fine.
> I've found very few projects where a bit of poking can't turn up memory safety issues.
I'm working on a Rust project right now, and I'm probably one of those people who are overestimating the correctness of my code! I would love to know about what sorts of memory safety issues you often uncover.
Made it more clear in the original post that I was talking about the correctness of C and C++ code. I haven't observed any notable issues with this in Rust compared to similar languages, but I also don't have the same depth of experience building large systems in it to show me the error of my ways yet.
The overall argument that Rust's use in embedded has a long way to go is fairly accurate. A foundational crate like embedded-hal reached 1.0 only 2 months ago, 8 years after Rust 1.0. On the other hand, this crate reaching 1.0 means the rest of the ecosystem can now mature alongside it.
The paper could use a few minor corrections and improvements. For example, they go through thousands of crates in crates.io to give the impression of completeness, but it would have been better to go though a more curated source like https://github.com/rust-embedded/awesome-embedded-rust. It doesn't matter if the quality of random one off crates that someone hacked over a weekend is poor, it does matter if the crates recommended by the community are poor. But I suppose their analysis would look impressive then.
The other nit is that they think that the existence of even one instance of unsafe is a problem. This is a mistake that people unfamiliar with Rust make.
As a rust programmer that programs C++ on embedded, the main thing preventing adoption is the ecosystem.
So let's say I happen to work with a MCU that is well supported in Rust, what if I want to connect it to a popular OLED display? Is there a library for that? If so, does it work? If it works, does it have the needed features?
Now maybe I am incredibly lucky and all of that works, what about a popular gyro IC?
Granted, there is probably some way to interface the Rust code with C code, but is that gonna work without turning it into a day of research?
I will certainly check back on the state of Rust on embedded every now and then, but as of now C++ is my goto.
From my experience in the embedded world, the ecosystem doesn't seem super relevant. You may depend on certain libraries for encryption, compression, etc. but you're more likely than not to be writing drivers for all of your devices more or less from scratch.
Other than Arduino, which of course has pretty good support for many devices and is likely to work out of the box.
It probably depends on what you do with embedded, I can certainly imagine situations/jobs where you are correct, but I am not in that situation, unfortunately. I can't afford the time to write my own drivers unless I really need/want to.
I run an electronics workshop at an artschool, so the end result counts and how we get there is more or less arbitrary unless it happens in a finite timeframe. For my own private projects I tried Rust, but also there my goal is to get things done, not to fool around forever. I like fooling around with the interesting bits, not with rewriting a thing others have written before.
You will probably be on your own unfortunately regarding hardware support. Even when libraries exist, they are often not worth the fork/PR/modification process to accommodate your use case. I hope you like datasheets... This sounds rough, but I will highlight two upsides:
#1: You benefit from the nice tooling and language of rust
#2: You are forced to learn how to use peripherals at a lower level than would otherwise be required. You will become adept at reading datasheets, and interacting with hardware, because you will write to registers directly while building your own abstractions.
At least for the ssd1306 and the sh1106 there is in fact a library for it that works well.
I recently started working on a rust-esp32 project because I'm far too lazy to properly deal with json in C. It was basic, some buttons, a display, an encoder, and a web api but it was a breeze. I'd really like to continue on more complex projects in the future. I've somehow avoided writing C++ in an embedded setting and now I'm afraid I might have to go back and learn that rather than continue with Rust.
Industry is firmly in C/C++ land and I'm trying to pivot towards a more embedded career, which would put me in more junior jobs without the political sway to say "lets try embedded rust and contribute back".
There's a couple forward thinking companies like memfault, but it seems to be the minority.
My degree was in CE so I've always played around with embedded projects, but actual design skills have stagnated from being in my field, which trends towards only developing smaller purpose built tools for internal use over products.
The semantics as you cross the boundary are ill-defined. And you will pay dearly if the C library wants to own any of the resources (like an event loop).
And "unsafe" programming in Rust is really difficult to get right--possibly harder than C.
To be fair, these problems are not inherent to Rust--any language which tries to interface with C will have them.
Two C libraries interface just fine if library 1 handles ownership and library 2 (or your program) simply operates on the resulting things being passed around.
That 2019 blog post explains how, having made a whole series of mistakes, they're now going to rewrite everything in C and it'll go much better. The next blog post says they've abandoned the project instead.
To me, it seems like they'd lost motivation years previously and were increasingly flailing around for something that would spark the same joy as initial work on an exciting new project, and Rust has little to do with that.
Lots of crates exist which wrap popular C libraries, and they don't seem to run into the sort of misery you describe.
So the author tried very hard to provide a safe interface for things that are inherently unsafe and found it to be too much work. That’s similar but different to just calling C from Rust and living with unsafe code like you normally do in C.
> As a rust programmer that programs C++ on embedded, the main thing preventing adoption is the ecosystem.
Maybe the mindset of the industry as well?
Not sure if things have changed (and if so, how) but 9-10 years ago when i was in university I had an interview with a company that did embedded systems stuff. While talking about my competencies I mentioned I was able to create cross-toolchain if they were interested and the guy (he was kinda like the CTO iirc) abruptly interrupted me and just said:
"no. just no. we always and only use the vendor's BSP (board support package). we don't care about anything else. should there be any kind of issue we want to be able to ring them and get them to fix the issue.".
How do you get people (or an industry?) with such a mindset to just use something because it's trendy?
My guess is that industry will wait another 10-15 years until some vendor big enough ships a rust toolchain as a BSP. Other vendors will follow. Then Rust on embedded systems will flourish.
Ah yes, then the vendor goes out of business and they have to bring in highly paid consultants to fix bugs in the vendors' gcc 2.95/Linux 2.6.x port to their SoC.
You need to be in the right kind of company - the one where waiting for the external vendor to fix their shit is too slow.
What? Are there really people out there relying on libraries for every peripheral? I've never seen anyone use a third party library for peripherals like that, short of some prototyping on an Arduino or the like. It's always just drivers implemented from scratch.
Jup. This is my field of work. I am ot a full time embedded developer, I run an electronics workshop at an artschool. So a lot of arduino-ish things, and aometimes more sophisticated stuff. But I ain't got no time to write drivers.
Honestly i have a far easier time finding working no-std libraries for anything i need on embedded Rust compared to embedded C++. Arduino ecosystem is an exception, but that's not necessarily something i'd put in production
The paper may be Rust specific, but I found the CVE break down chart on p. 25 interesting. When looking at the percentages of the CVE causes (focused on the 59 bugs classified as those Rust prevents) I got the following:
Out of Bounds Reads => 18.6%;
Out of Bounds Writes => 62.7%;
Null-Ptr Deref => 8.5%;
Use-After-Free => 5.1%;
Type Confusion, Uninitialized Pointer Access, Memory Leak => EACH 1.5%;
I have to wonder about the applicability of these percentages to non-RTOS programming. I find it very interesting that 81% of CVE's are allocated to Out-of-Bound Read/Writes, with writes being the larges percent of those obviously.
Has there been any CVE cause analysis performed and publicly available? If so and the percentages bear out similarly to RTOS's across a spectrum of application/system types then there may be some clear cost/benefit analysis needed at the programming language design stage. Rust is a complex language with a complex type and lifetime system to achieve memory safety, and it is not an uncommon refrain that a simpler 'safe' language would be appreciated by many developers. If 80% of CVE's come from Read/Write errors on array access, then a language that enforces strict memory access semantics, but forgoes the rest of Rust's complexity regarding lifetimes and type system complexity would achieve a very large portion of exploit prevention at a minimal cost.
Additionally, if you prevent Null types in the type system the language would then prevent 90% of CVE causes, again with a minimal amount of complexity.
I'm not certain that the above is correct, if the percentages play out in the large, or that devs would actively switch to a considerably safer, while being simpler language. But it certainly is thought provoking.
I'm with you on that. In fact I think it's needed to use something less complex than rust in order to prevent other bugs from cropping up due to misunderstood parts of language.
C has some bad things it does by default that lead to terrible bugs. I imagine many of them can be addressed without complex move semantics added to it.
Some promising work I've seen in this area had been the adoption of language level allocators in zig and Odin. Many newer languages also have better arrays come with length information. And array languages like APL avoid out of bounds errors.
I don't think you have to go full ML style type checker (or borrow checker) to prevent bugs.
A bounds-checked slice type would be a relatively small addition to C, and if adopted, it would make size tracking and bounds checking easier.
However, there's generally a strong pushback from C developers against features that have an unnecessary performance overhead.
Having reliable bounds checking without run-time cost if a much more complicated problem. Rust uses iterators for this, but that requires generics and specialization, and compared to C, these are big and complex features.
To be clear, I don't think there is any hope of implementing any protections at the language level in C, the push back would be exceptionally fierce. Although I do agree that a 'slice' type in the stdlib would not be to much to ask for.
My comment was more for consideration in the design of new languages, in particular, the development of the frequently cited as not existing, simple, C-like language with memory safety features. In that case, in a green field scenario, there are a few ways to achieve statically known memory-boundary respecting iteration and general access. Further, there are several existing methods of achieving 'generics'. The real design challenge would be in finding the simplest implementation that does not overly burden potential developers.
I am confident that it could be done, but it would take some grave dissatisfaction with Rust (which is currently at the top of the adoption curve in the memory safe, but GC free space) for the proposed language to take off.
The problem with slices in C being added in stdlib would be that they really are a generic data type. It would be similar to atomic types. While C++ could just add std::atomic<T>, C needed to add a special construct: _Atomic(T).
C++ already has a slice type. It’s called std::span<T>. Porting it to C would probably require something similar to atomics. At which point I guess you may just as well get on with it and switch to C++.
C++ chose not to have bounds checking on span’s operator[] by default. Like with vector, approximately nobody will use .at().
Buffer overflow vulnerabilities are where programmers thought they don’t need a bounds check, so having this as a choice every time, with the safer option looking worse, is working against it.
A nice minimal built-in syntax for spans (like Rust’s slices) could be an incentive to keep the bounds checks, and eventually make bare pointer arithmetic stand out as riskier.
I think this is more of a social problem with C++. Regardless whether the feature is implemented in the standard library or the core language, the broader C++ community is not going to approve of enabling bounds checks on the bracket operator. And whether this operator is implemented as a builtin or as an overload on an stdlib type is basically transparent to the user.
FWIW you can enable bounds checks for the bracket operator by default for STL types in GCC and Clang. So for interfacing with Rust it is both obvious how to translate between slices in Rust and std::span in C++, and you can verify to a degree that this std::span isn’t corrupted on C++ side (to a degree).
Correct me if I'm wrong, but the percentages on page 25 are lower then what you listed?
Out of Bounds Reads => 10.1%;
Out of Bounds Writes => 34%;
Null-Ptr Deref => 4.6%;
Type Confusion => 0.9%
Uninitialized Pointer Access => 0.9%
Use-After-Free => 2.8%;
Memory Leaks => 0.9%;
The most staggering statistic is the out of bounds writes. C23 added variably-modified types which helps [1], but I hope future revisions of C consider adding slices. I quite like Zig slices where a "slice" can be constructed from any pointer+length pair.
The stats you listed are for the percentage of CVE causes out of the total reviewed CVEs (109), which includes those CVE causes Rust can prevent and those that are completely language independent causes. I took the stats for those CVEs that Rust is said to prevent, which according to the paper is 59 CVEs. So I took the number of any given cause and took its percentage out of the 59 CVEs that Rust's memory safety guarantees would prevent.
In that list, Rust’s borrow checker only solves use-after-free and memory leaks. The other items in the list can be solved without it (null dereference, out of bounds read and write, etc.). See Zig for example.
Personally in the past ~2 years I have been trying to shift my code base to rust when it comes to robotics and drones software, the biggest issue is the integration part, most “addons” that you can integrate with the robots like Lidar and other sensors come with the usual SDKs in C/C++ or even python. Additionally, most of X-rust converters don’t really work so you end up rewriting it from scratch.
Nothing is wrong with that, it’s rather a workaround, ultimately I am trying to have one language only including the UI too (been playing with egui),so I don’t have to use JavaScript.
>that you can integrate with the robots like Lidar and other sensors come with the usual SDKs in C/C++ or even python.
Do the Python SDKs run fast enough? Genuine question. I've done a good amount of Python, but don't have any idea of how suitable it is for embedded stuff, other than knowing that MicroPython exists.
A lot of parts I had to work with didn’t have it, last one a couple months ago for example was a guided parachute for a drone dropper, I ended up making the driver from scratch that interfaced with the serial io.
2. there are tools to convert c to rust (I dont know if I'd trust this..)
3. "Out of 43 different MCU families, peripheral crates are currently available for only 16 (37%). Most of these crates are generated using svd2rust utility"
4. developers considered but rejected rust because: "Lack of Support for MCUs (36%) ; Difficulty Integrating with Existing codebase (32%) ; Organization constraints and certification requirements (30%)"
5. "The second major (26%) issue is debugging, which is expected because, as explained in Section 2, embedded systems follow an asynchronous and event-driven design. This results in frequent cross-language domain interactions and makes debugging hard."
I would adopt rust if it were easy to get up and running just while(1) loop applications.
> 2. there are tools to convert c to rust (I dont know if I'd trust this..)
The core C specification by itself isn't all that complicated of a language; a C-to-Rust transpiler is a pretty doable project.
The main issues here are that
a) a lot of the code you'd likely want to convert is likely to be reliant on non-standard extensions
b) there's a lot of undefined behavior which you probably want to have somewhat more defined behavior on, especially in embedded contexts
c) the real goal for a lot of this automated conversion is to do the conversion once and work well enough that you don't have to audit the result of the conversion, and because of especially the previous point, it's really hard to get that level of trust for C code.
The existing c2rust converter works by creating the clang AST and then lowering that to Rust source code, which I'm not sure is a path that would lead me to high confidence in the converted code due to the potential impedance mismatch in understanding the clang AST. A custom C frontend is probably a better match here for a long term project (C, unlike C++, is feasible to build your own compiler from scratch), or maybe another project idea is to convert LLVM IR to Rust and ditch the C frontend entirely.
> C-to-Rust transpiler is a pretty doable project.
It's been done, but what comes out is terrible Rust. Everything is unsafe types with C semantics.
An intelligent C to Rust translator would be a big win. You'd need to annotate the input C with info about how long arrays are and such, to guide the translator. It might be possible to use an LLM to analyze the code and provide annotations. Usually, C code does have array length info; it's just not in a form that the language ties to the array itself. If you see
char* buf = malloc(len);
the programmer knows that "buf" has length "len", but the programmer does not. Something needs to annotate "buf" with that info so that the translator knows it. Then the translator can generate Rust:
let mut buf = vec![0;len];
The payoff comes at calls. C code:
int write_to_device(char* buf, size_t len)
is a common idiom. LLMs are good at idioms. At this point, one can guess that this
is equivalent to
fn write_to_device(buf: &[u8]) -> i32
in Rust. Then the translator has to track "len" to make sure that
assert_eq!(buf.len(), len);
is either provably true, or put in that assert to check it at run time.
So that's a path to translation into safe Rust.
Funding could probably be obtained from Homeland Security for this, given the new White House level interest in safe languages and the headaches being caused by the cyber war. Is CVS still down?
> It's been done, but what comes out is terrible Rust. Everything is unsafe types with C semantics.
The idea behind the current c2rust tool is that you'd do a one-shot conversion to Rust and then gradually do refactoring passes over the barely-Rust code to convert it to correct C code. The focus is on preserving semantics of C over writing anything close to idiomatic (cue a + b being translated to a.wrapping_add(b) all the time, e.g.). Which is an approach, but I'm not sure it ends up providing any value over "set your system to compile both C and Rust into a final image and then slowly move stuff from the C to the Rust side as appropriate" in practice.
> Usually, C code does have array length info; it's just not in a form that the language ties to the array itself.
This is actually why C23 made VLA support semi-mandatory: it enables you to describe a function signature as
int write_to_device(size_t len, char buf[len])
and C23 compilers are required to support that, even in absence of full VLA support! The intent of making this support mandatory was to be able to use that as a basis for adding better bounds-checking support to the language and compilers. (Although, as you noticed, there is an order-of-declarations issue compared to the typical idiomatic expression of such APIs in C, and the committee has yet to find a solution to that).
> The idea behind the current c2rust tool is that you'd do a one-shot conversion to Rust and then gradually do refactoring passes over the barely-Rust code to convert it to correct C code.
I've seen what comes out of the transpiler. Nobody should touch that code by hand. It's awful Rust, and uglier than the original C. Modifying that by hand is like modifying compiler-generated machine code.
> This is actually why C23 made VLA support semi-mandatory.
C23 doesn't actually use that info. You can't get the size of buf from buf. I proposed something like that 12 years ago.[1] But I wanted to add enough features to check it.
> I've seen what comes out of the transpiler. Nobody should touch that code by hand. It's awful Rust, and uglier than the original C.
I can't disagree here. I think the original idea was to rely on automated refactoring tools to try to make the generated Rust somewhat more palatable, but I never was able to get that working.
> C23 doesn't actually use that info.
True; the intent is to require it so that it can be leveraged by future extensions. The C committee tends to move glacially.
The real problem is not translating code. It's translating data types. If you can determine that a "char *" in C can be a Vec in Rust, you're most of the way there. It's no longer ambiguous what to do with the accesses.
This is where I think LLMs could help. Ask an LLM "In this code, could variable "buf" be safely represented as a Rust "Vec", and if so, what is its length?. LLMs don't really know the languages, but they have access to many samples, which is probably good enough to get a correct guess most of the time. That's enough to provide annotation hints to a dumb translator. The problem here is translating C idioms to Rust idioms, which is an LLM kind of problem.
They have made some improvements here recently. There is a lot less unsafe generated. The rest is more idiomatic too. The cost is that it will be throwing panics everywhere until you fix the faulty assumptions it asserted. I like the new way better.
The big problem is just the fundamental mismatch between what Rust requires and what you can do in C, especially with embedded code.
If the library in question handles interrupts by jumping to an interrupt handler that updates some shared state you're going to have a bad time converting that into safe Rust.
> 3. "Out of 43 different MCU families, peripheral crates are currently available for only 16 (37%).
There are a lot of obscure MCU families out there. Most engineers or shops specialize in a couple, become familiar with those, and stick to it. Using and learning a brand new MCU family is a lot of work.
As long as I can find Rust support for common MCUs that I use, I don’t care how broadly the rest of the market is covered.
It would need to be an historic backorder to make me switch from e.g. STM32 to like a C2000. Or honestly from an STM32F to STM32H.
For those how don't know, its very rare for a production device to use a socket for its MCU. Most MCUs don't even come in packages that support sockets. They're always soldered in. So unless you're switching to another MCU whose pinout, external clock, and power supply requirements are close enough that the hardware change is really just a BOM change, switching MCUs in case of a shortage is not a realistic option. Reportedly, some manufacturers were buying entire washing machines during the height of the chip shortage, just to de-solder the machines' MCUs and use them in their own products. That that is the better option should tell you how painful changing MCUs can be.
And that's to say nothing about porting the firmware, which may, or may not, be trivial.
In general I get the impression that embedded rust is fairly good for while(1) loop applications: the kind of thing you can do with arduino is also usually fairly easy to do in rust, modulo maybe not so good library support for random bits of hardware. What I generally see lacking is support for multitasking: the various HALs generally only support synchronous, usually only busy-loop blocking implementations, which is really limiting for a lot of embedded applications. This kind of thing is hard to get right, though.
(in fact, while I like the theory of generic embedded HALs, I have yet to see a good implementation of the concept. Most effective HALs I have seen are specialised to one area or another, usually to one particular application)
As an aside, does anyone know if there is a central directory of these Rust "areweXyet" websites? I didn't know of this one but I knew about https://areweideyet.com/ and https://areweguiyet.com/
> I would adopt rust if it were easy to get up and running just while(1) loop applications.
It is: Install the toolchain (eg `rustup target add thumbv7hibf`); install probe-rs; `cargo run`. I think getting applications up and running in embedded rust is one of its strengths.
"Embedded" is a diverse concept. No need for Rust on ATtiny with all variables static and Harvard architecture. In fact, even C often is overkill, and Assembler is a better choice for a simple LED flasher or really tight high performance loop.
I think when bringing up embedded rust it is necessary to specify an application.
For low level, hard realtime control and interrupt handling rust gets in the way. Many embedded applications stop here. For things like parsing, protocol stacks and business logic rust has a clear advantage. Interoperability with C is therefore essential. The current situation is good for ARM and RISC (ESP32) but impossible for weirder stuff like C28x. (See my demo here: https://github.com/driftregion/bazel-c-rust-x86_linux-armv7_... )
If you're using Stm32s or Nordiks (among a few others) then I cannot recommend enough the Embassy framework.
I used it for a custom board I made and it not only consumed less power overall due to the automatic chip sleep state handling but has been drastically easier to work with than the provided C firmware.
Developer is also super responsive on Matrix and a nice guy.
I spent a weekend with Rust, a Raspi4, and our buddy GPT about six months ago. In that weekend I was able to get the Raspi controlling a OLED display via SPI with an SSD1306 controller. I thought it was a fairly clean port from C++, and GPT was well educated on how to use the RASPI SPI and I2C busses from Rust.
I don't think I would be able to approach it on an ESP32 or AVR xMega or some other real microcontroller.
Honestly, the biggest thing that concerns me with using Rust for embedded is the size of the crates. We were looking to do some packages for a product, and the Rust packages were huge compared to the C++ ones. Granted, this was mostly because the C++ ones could use .so's, while Rust had to compile those into the crate, but this is a huge issue when doing OTA updates.
It's just something you have to care about, but it's not a show-stopper. We use a bunch of crates in our projects at work, I left some example sizes in a comment a while back https://news.ycombinator.com/item?id=34032824
It's a show stopper when size matters and you can't fit the binaries into flash.
I'm sure "sorry for getting everyone to switch to this unestablished language" will go over very well with your boss and upper management. At least in C and C++ you can blame your tools.
If you've stupidly convinced management the existing tooling is shit, then you've got a problem. And I don't mean a technical one, I mean a problem with paying rent, because you're not going to be employed much longer.
That's great it works well for AMD64 Linux ELF files. I would be surprised if it didn't given the toolchain seems to have been designed around that triple specifically.
But the majority of embedded platforms are not booting or running AMD64 Linux, they're running ARMv{6,7,8}, MIPS, or 32-bit RISC-V chips.
This is not for Linux, these are for the RTOS that we have authored. Bare metal 32 bit ARM.
Basically, you've confused the two examples: the first one is about x86_64 Linux, yes, but the rest are all thumbv6m-none-eabi, thumbv7em-none-eabihf, thumbv8m.main-none-eabihf, stuff like that.
I've been trying out embassy-rs and what is really exciting that you might get RTOS abilities, without actually using an RTOS. Just native Rust, with some smart abstractions. Still prefer C, but the Rust embedded community seems to be cooking up something very interesting.
What do you mean by visually debug? If you install `probe-rs` and do `cargo run`, you can print whatever you want to console; not related to the IDE. (Not sure if this is what you're looking for, or something else)
Some of the survey responses highlight one of the biggest hurdles to rust adoption I've experienced though:
Rust has an education problem. People dramatically overestimate the correctness of their [C/C++] code and underestimate the potential severity of failures to meet that expectation. I've found very few projects where a bit of poking can't turn up memory safety issues. Even people who are far more skilled programmers than I am routinely write silly mistakes.
Unfortunately the response I often hear to bringing these issues up after the code exists is "it's working code, why should I care about these theoretical issues?"