
Posted by Jeff Vander Stoep, Android Last year, we wrote about why a memory safety strategy that focuses on vulnerability prevention in ...
Last year, we wrote about why a memory safety strategy that focuses on vulnerability prevention in new code quickly yields durable and compounding gains. This year we look at how this approach isn’t just fixing things, but helping us move faster.
The 2025 data continues to validate the approach, with memory safety vulnerabilities falling below 20% of total vulnerabilities for the first time.
Updated data for 2025. This data covers first-party and third-party (open source) code changes to the Android platform across C, C++, Java, Kotlin, and Rust. This post is published a couple of months before the end of 2025, but Android’s industry-standard 90-day patch window means that these results are very likely close to final. We can and will accelerate patching when necessary.
We adopted Rust for its security and are seeing a 1000x reduction in memory safety vulnerability density compared to Android’s C and C++ code. But the biggest surprise was Rust's impact on software delivery. With Rust changes having a 4x lower rollback rate and spending 25% less time in code review, the safer path is now also the faster one.
In this post, we dig into the data behind this shift and also cover:
Developing an operating system requires the low-level control and predictability of systems programming languages like C, C++, and Rust. While Java and Kotlin are important for Android platform development, their role is complementary to the systems languages rather than interchangeable. We introduced Rust into Android as a direct alternative to C and C++, offering a similar level of control but without many of their risks. We focus this analysis on new and actively developed code because our data shows this to be an effective approach.
When we look at development in systems languages (excluding Java and Kotlin), two trends emerge: a steep rise in Rust usage and a slower but steady decline in new C++.
Net lines of code added: Rust vs. C++, first-party Android code.
This chart focuses on first-party (Google-developed) code (unlike the previous chart that included all first-party and third-party code in Android.) We only include systems languages, C/C++ (which is primarily C++), and Rust.
The chart shows that the volume of new Rust code now rivals that of C++, enabling reliable comparisons of software development process metrics. To measure this, we use the DORA1 framework, a decade-long research program that has become the industry standard for evaluating software engineering team performance. DORA metrics focus on:
Cross-language comparisons can be challenging. We use several techniques to ensure the comparisons are reliable.
Code review is a time-consuming and high-latency part of the development process. Reworking code is a primary source of these costly delays. Data shows that Rust code requires fewer revisions. This trend has been consistent since 2023. Rust changes of a similar size need about 20% fewer revisions than their C++ counterparts.
In addition, Rust changes currently spend about 25% less time in code review compared to C++. We speculate that the significant change in favor of Rust between 2023 and 2024 is due to increased Rust expertise on the Android team.
While less rework and faster code reviews offer modest productivity gains, the most significant improvements are in the stability and quality of the changes.
Stable and high-quality changes differentiate Rust. DORA uses rollback rate for evaluating change stability. Rust's rollback rate is very low and continues to decrease, even as its adoption in Android surpasses C++.
For medium and large changes, the rollback rate of Rust changes in Android is ~4x lower than C++. This low rollback rate doesn't just indicate stability; it actively improves overall development throughput. Rollbacks are highly disruptive to productivity, introducing organizational friction and mobilizing resources far beyond the developer who submitted the faulty change. Rollbacks necessitate rework and more code reviews, can also lead to build respins, postmortems, and blockage of other teams. Resulting postmortems often introduce new safeguards that add even more development overhead.
In a self-reported survey from 2022, Google software engineers reported that Rust is both easier to review and more likely to be correct. The hard data on rollback rates and review times validates those impressions.
Historically, security improvements often came at a cost. More security meant more process, slower performance, or delayed features, forcing trade-offs between security and other product goals. The shift to Rust is different: we are significantly improving security and key development efficiency and product stability metrics.
With Rust support now mature for building Android system services and libraries, we are focused on bringing its security and productivity advantages elsewhere.
These examples highlight Rust's role in reducing security risks, but memory-safe languages are only one part of a comprehensive memory safety strategy. We continue to employ a defense-in-depth approach, the value of which was clearly demonstrated in a recent near-miss.
We recently avoided shipping our very first Rust-based memory safety vulnerability: a linear buffer overflow in CrabbyAVIF. It was a near-miss. To ensure the patch received high priority and was tracked through release channels, we assigned it the identifier CVE-2025-48530. While it’s great that the vulnerability never made it into a public release, the near-miss offers valuable lessons. The following sections highlight key takeaways from our postmortem.
A key finding is that Android’s Scudo hardened allocator deterministically rendered this vulnerability non-exploitable due to guard pages surrounding secondary allocations. While Scudo is Android’s default allocator, used on Google Pixel and many other devices, we continue to work with partners to make it mandatory. In the meantime, we will issue CVEs of sufficient severity for vulnerabilities that could be prevented by Scudo.
In addition to protecting against overflows, Scudo’s use of guard pages helped identify this issue by changing an overflow from silent memory corruption into a noisy crash. However, we did discover a gap in our crash reporting: it failed to clearly show that the crash was a result of an overflow, which slowed down triage and response. This has been fixed, and we now have a clear signal when overflows occur into Scudo guard pages.
Operating system development requires unsafe code, typically C, C++, or unsafe Rust (for example, for FFI and interacting with hardware), so simply banning unsafe code is not workable. When developers must use unsafe, they should understand how to do so soundly and responsibly
To that end, we are adding a new deep dive on unsafe code to our Comprehensive Rust training. This new module, currently in development, aims to teach developers how to reason about unsafe Rust code, soundness and undefined behavior, as well as best practices like safety comments and encapsulating unsafe code in safe abstractions.
Better understanding of unsafe Rust will lead to even higher quality and more secure code across the open source software ecosystem and within Android. As we'll discuss in the next section, our unsafe Rust is already really quite safe. It’s exciting to consider just how high the bar can go.
This near-miss inevitably raises the question: "If Rust can have memory safety vulnerabilities, then what’s the point?"
The point is that the density is drastically lower. So much lower that it represents a major shift in security posture. Based on our near-miss, we can make a conservative estimate. With roughly 5 million lines of Rust in the Android platform and one potential memory safety vulnerability found (and fixed pre-release), our estimated vulnerability density for Rust is 0.2 vuln per 1 million lines (MLOC).
Our historical data for C and C++ shows a density of closer to 1,000 memory safety vulnerabilities per MLOC. Our Rust code is currently tracking at a density orders of magnitude lower: a more than 1000x reduction.
Memory safety rightfully receives significant focus because the vulnerability class is uniquely powerful and (historically) highly prevalent. High vulnerability density undermines otherwise solid security design because these flaws can be chained to bypass defenses, including those specifically targeting memory safety exploits. Significantly lowering vulnerability density does not just reduce the number of bugs; it dramatically boosts the effectiveness of our entire security architecture.
The primary security concern regarding Rust generally centers on the approximately 4% of code written within unsafe{} blocks. This subset of Rust has fueled significant speculation, misconceptions, and even theories that unsafe Rust might be more buggy than C. Empirical evidence shows this to be quite wrong.
Our data indicates that even a more conservative assumption, that a line of unsafe Rust is as likely to have a bug as a line of C or C++, significantly overestimates the risk of unsafe Rust. We don’t know for sure why this is the case, but there are likely several contributing factors:
Historically, we had to accept a trade-off: mitigating the risks of memory safety defects required substantial investments in static analysis, runtime mitigations, sandboxing, and reactive patching. This approach attempted to move fast and then pick up the pieces afterwards. These layered protections were essential, but they came at a high cost to performance and developer productivity, while still providing insufficient assurance.
While C and C++ will persist, and both software and hardware safety mechanisms remain critical for layered defense, the transition to Rust is a different approach where the more secure path is also demonstrably more efficient. Instead of moving fast and then later fixing the mess, we can move faster while fixing things. And who knows, as our code gets increasingly safe, perhaps we can start to reclaim even more of that performance and productivity that we exchanged for security, all while also improving security.
Thank you to the following individuals for their contributions to this post:
Finally, a tremendous thank you to the Android Rust team, and the entire Android organization for your relentless commitment to engineering excellence and continuous improvement.
5 million Rust LOC
One potential memory safety vulnerability found
Rust is 0.2 vuln per 1 MLOC.
Compared to
C and C++ : 1,000 memory safety vulnerabilities per MLOC.
Key take.There are certain places on the internet where any mention of rewriting in Rust is met with scorn and ire. And while, like any technical decision, there are pros and cons, I cannot see why in the face of astounding evidence like this, you would completely dismiss it.
And I say this as someone who has never written a line of Rust in their life (some day I'll find the time).
In my experience, the loudest critics of Rust I have heard are actually seasoned C and C++ developers who are incredibly good at one or both languages, but know almost nothing outside it.
On one hand, C++ is an incredibly complicated language that one can invest considerable amounts of time into. It also used to occupy a unique niche where you get tons of abstraction features yet as much blazing speed as you care to spend time optimising. Rust is encroaching on that niche.
On the other hand, C is an incredibly simple language, which does not allow for convenient expressions of particular abstractions. If they hated C++ because it was too hard to follow. What the code is doing, they probably hate rust for the similar levels of abstraction affordances.
When I hear the bad faith arguments from people who really should know better, what I hear is a group of scared developers who have been able to coast on their knowledge base for years or even decades, and are now feeling like their skill set is at risk of being unnecessary.
It always seemed like an unproductive attitude to have in a knowledge-based industry like software development. I am also a C++ developer, but you bet I am learning Rust on the side, because I think it's a good idea to skate where the puck is headed. I also learned enough of Zig to be dangerous a few months ago because it was fun.
Either way, I would suggest those developers reflect on the reason why they have this reflexive need to throw a wrench into any conversation mentioning rust. If their intention is to slow down adoption through hostile community sentiment, it's not working.
> what I hear is a group of scared developers who have been able to coast on their knowledge base for years or even decades
That’s certainly not the case for C++. The C++ language has evolved quickly, with a release every three years or so. One could coast, but they would be writing outdated C++ that no newcomer likes. That is, the entire organization needs to also coast for this behavior to be viable.
Instead I see most of the bad faith criticisms of Rust coming from aficionados of other languages in roughly the same space such as Zig or Nim, or occasionally Go. They whine because they think Zig or Nim should take the place of Rust.
>Instead I see most of the bad faith criticisms of Rust coming from aficionados of other languages....
>They whine because they think Zig or Nim should take the place of Rust.
How about because Rust Evangelism Strikeforce go after other language first? Every time a language in the same space comes they get asked if they are memory safe?
Things have died down now, doesn't mean it didn't happen.
> Things have died down now, doesn't mean it didn't happen.
I have some bad news for you. In the early 90's, C++ _was_ that language. Not only that, but at the time a lot of the reactionary criticisms of C++ by annoyed C developers had a lot more weight to them.
- C++ was slower than C, because the costs of vtables and exceptions was a lot larger and compilers weren't as good at optimizing.
- C++ was bug-ridden, both because it was easy to accidentally misuse the language, but also because compilers were simply buggy due to the complexity of the language.
- Many of the STL containers you know and love were missing from pre-standard C++, and even afterwards its implementation was commonly subpar, leading developers to either use their compiler-specific proprietary containers or roll their own.
- Streams were often the only reliable thing in the C++ standard library. It also ballooned compile times to the point of being outright banned from many codebases.
- Don't get me started on the utter nightmare that was sifting through compiler errors through misuse of templates...
So yeah, I don't want to hear other C++ developers whine about how annoying Rust developers are. We were just as annoying back in the day, with a worse language. Linus banned C++ from the kernel, and honestly he was 100% right to do so at the time.
> Every time a language in the same space comes they get asked if they are memory safe?
So what?
It might be shocking for you, but every new messenger gets asked if it supports e2ee these days.
Why shouldn't people want to know about the availability of a massive advancement in the space of system programming languages?
> The C++ language has evolved quickly, with a release every three years or so.
There was a revival of C++ starting with C++11 and ending around the era of C++17. It was, frankly, great. However, a few things happened around and since C++20 that has caused the language to backslide.
First, it seems like corporate support for C++ has dropped significantly. Apple, having successfully replaced GCC with Clang for their own internal use, no longer cares that much about keeping up with the latest C++ features, choosing to prioritize Swift instead. Google seems to have slowed their contributions after being blocked from any commitments that opened up the possibility of breaking ABI. Microsoft has gutted their Visual C++ team, which has significantly slowed the implementation pace of new features and fixing bugs...unless they're related to Unreal Engine. There are also persistent rumors going around that Microsoft are working on their own project in the spirit of rustc-codegen-gcc, but for Visual Studio.
Second, it seems like a lot of the new blood that the revival decade had attracted to the C++ community and standards committee have stopped participating and given up. There's probably not one or two all-encompassing reasons for this, but the disconnected stories I keep hearing about just leave me feeling sad for the future of C++. Anecdotally, the ones that I see that are most commonly cited up are the horrendous rollout of C++ modules, the drama surrounding Safe C++ vs Safety Profiles, and the circling of the wagons around rot13_content_warning("pbaivpgrq puvyq encvfg") Arthur O'Dwyer.
If I had to pick one own-goal in particular that ground my gears from a technical perspective, it's the ordeals of the developer who tried to get `std::embed` into C++. Going through the standardization process was such a nightmare for her - including trying to convince the standards committee that it was a problem worth solving in the first place - that she eventually gave up and took the idea to the C standards committee instead. After a few rounds of back-and-forth feedback, C23 now has #embed. Not only did C get the feature before C++, but the C++ standards committee gave up whatever say they had in the shape of the final product.
That's not to say that I'm not looking forward to certain modern C++ features. Concepts are great, even if they fall well short of the original proposal. I use std::span and std::expected in every project I can, even if I have to use third-party polyfill libraries to deliver them. Modules could be nice when they're more widely supported. But _man_ the future of C++ looks pretty bleak from where I'm sitting, and it seems like the people in a position most able to do something about that future aren't making wise decisions.
Agree with a lot of what you said. Definitely to a lot of corporations C++17 was the peak of the language. Apple prioritized Swift afterwards and Google started doing Carbon and begrudgingly accepted Rust.
First time hearing about the embed issue and god that person’s experience is awful: https://thephd.dev/finally-embed-in-c23 and it feels like a job for only the best project managers.
An earlier Google blog post from the same series (link in the first sentence) pointed out why: new code tend to have more vulnerabilities than established code. So it makes more sense to write new code in Rust than to rewrite old code in Rust. After all new features are still being added and new code needs to be written; it’s not like the codebase is done with features.
According to that blog post (https://security.googleblog.com/2024/09/eliminating-memory-s...), the vulnerability density for 5 year old code in Android is 7.4x lower than for new code. If Rust has a 5000 times lower vulnerability density, and if you imagine that 7.4x reduction to repeat itself every 5 years, you would have to "wait" (work on the code) for... about 21 years to get down to the same vulnerability density as new Rust code has. 21 years ago was 2004. Android (2008) didn't even exist yet.
Remember that there are other types of vulnerabilities too. If there are less of them in old code then it may make up for more memory issues.
I also feel like this is good advice when making a language shift, or any _other_ shift, even a stylistic one.
A lot of my coworkers get in this situation where, when a change in direction is made, they feel like we have to stuff the roadmap with work to rewrite everything. That work is... 0 value, in most cases, unless the decision we have made is intended to directly solve an issue in the existing code.
Many times I find that if you just do new work in the new thing, you'll "naturally" prioritize rewriting at the right rate. When we do it that way, we end up replacing, rather than rewriting, those legacy systems, which avoids the pitfall of trying to reproduce prior behavior, down to the bugs it may have had.
>So it makes more sense to write new code in Rust than to rewrite old code in Rust.
This is a general result regardless of what language you're talking about (unless you're really downgrading to assembly or something crazy). This of course presumes that the overall Rust (or other new language) situation is better than the existing one. It's not generally.
The blog post has a number of issues, including mixing C and C++. And Android C++ source code is often filled with C-style code, especially the older the code, and is not always that good in several aspects.
As an example, from https://android.googlesource.com/device/generic/trusty/+/c3f...
Many of the files in that commit have a lot of C-style code, yet are classified as C++. C and C++ are very different programming languages, and memory safety is arguably significantly easier to achieve in practice in C++ than in C, yet in the blog post, C++ is blamed for C-style code, and C and C++ are not differentiated.
Compare and contrast with https://android.googlesource.com/device/generic/goldfish/+/d... . That source code file has much more modern C++. Though even then, it contains goto, and modern C++ code reviews would normally not accept goto in my experience. I do not understand what Google Android is doing when its developers are using goto. Could they not have used lambdas in those places where they are using goto? The mixture of std::string_view and goto, modern and yuck, is disconcerting.
On a different topic, how much of the new Rust code is vendored dependencies? Is Fuchsia included?
Maybe the real value for Google Android that Rust holds, is that it mostly prevents C-style code from being written. And Rust does not support goto, and while I think an argument could be made that goto is OK to include in a toolbox, its usage should be very, very, very, very rare. Why does somewhat modern Google Android C++ allow goto?
I am not impressed by Google Android's C++ code quality. Are Google Android developers, independent of language, significantly below average?
C++ does have baggage, cruft and issues. But having strange C++ code (like goto) and also blaming C++ for C-style code, does not help paint an honest and accurate image. And if Google Android's C++ code review process accepts goto willy-nilly, I do not consider Google Android to be at all credible on any subject related to code quality, memory safety and security in any programming language.
Thing is, from security point of view, if it is part of the ISO C++ PDF one can buy in Geneva, compiles with a C++ compiler in C++ mode, it is C++.
That argument can be applied to unsafe Rust as well. There are code reviews, coding standards and other checks for a reason.
Though, I suppose something like C++ profiles, just for modernization, might make it much easier to enforce and track that modern C++ is used.
The difference is that with Rust one can prevent unsafe in the compiler build settings.
Or any language with unsafe code blocks, which people keep forgetting also exist, while complaining about Rust, as if there isn't any other memory safe language.
With C++ you need external tooling to disable C like code, that a large part of the community refuses to adopt.
BTW; Do you know if it is possible to track new comments to Hacker News threads?
I used https://hnreplies.com/
> I cannot see why in the face of astounding evidence like this, you would completely dismiss it.
We're decades into the global warming era. You simply can't make some people accept evidence if the conclusions from that evidence would be uncomfortable to them.
If people don't take exploitability seriously, you can't make them. Well, unless you keep pwning them.
People are worried about half-assed[0] rewrites that break functionality and introduce exciting, new vulnerabilities due to improper implementation. And they aren't wrong to fear that, given the multiple issues we've seen in just the past week with Ubuntu's Rust overhaul.
[0]: Or even whole-assed. Memory (un)safety is only one form of vulnerability.
They were half-assed in the sense that they trusted the old test suite, which turned out to be a mistake, as some edge-cases weren't covered. Hopefully they will be more rigorous going forward, but even then, these bugs were caught before being added to a stable release, so overall fine.
I'm not sure if it is the best ROI to rewrite battle-tested tools in Rust, but it isn't like people are forced to do it. People have fun writing Rust, and want to work with it more, so that's the direction these projects take. And if you don't want to use these tools, the beautiful thing about Linux is that if you don't like the direction a distribution takes, you can always switch.
I think that it would be foolish for any software engineer to completely dismiss any technology. There is a time and place for any tool, and it is a job of a competent engineer to determine what the appropriate combination of these is that would solve a certain problem within specific constraints.
That said, memory safety is one criterion out of many that could be used to make that decision. For a large number of software projects, memory safety simply isn't a major concern. Ease of use, iteration speed, developer familiarity, availability of specific libraries, and so on, are often equal or greater concerns than memory safety.
So, sure, if you're writing a kernel, operating system, or a mission-critical piece of software, then Rust might be worth considering. Otherwise, you might be better served by other languages.
I dont so much dismiss the technology as the people who insist on rewriting everything in rust.
Rust seems to attract a certain mindset of mediocre programmers who yell "security" to shove their poorly written slower code down our throats.
Most of them seem to be former web developers who bring all their npm drama to stable C foundations
Stable where?
We're in C23 nowadays, and in Linux distributions there are plenty of npm like drama, one apt/dnf install away with pkg-config, or vcpkg/conan for the more modern folks.
Although I imagine there are a few still stuck in ./configure land.
Any C program shipped with GNU Autotools is fairly straightforward to package as a Deb or rpm
> I cannot see why in the face of astounding evidence like this, you would completely dismiss it.
Because it's not a silver bullet. That safety comes at a cost; Rust is much more difficult to learn than C or Zig and the compilation time for code with equivalent semantics is an order of magnitude greater. It has also added a great deal of toolchain complexity to projects like the Linux kernel.
People have decided that the pros outweigh the cons in those particular cases, but those cons exist nonetheless.
Rust is more difficult to learn the basics of than C, but I'm not sure it's more difficult to learn to write memory-safe code in Rust than in C. It's also not clear to me it's that much harder to learn Rust than it is to learn how to write equivalently-high-level code in C++ _unless you end up in one of the areas where Rust is really hard_. But a lot of systems code doesn't end up in those areas. Some does, and then you have to go fishing for a crate that does things for you. C++ just has a lot of corners you can get sucked into, and the legacy mix of multiple generations of C++ coding styles / OO stuff makes for some really tangled messes out there.
(fwiw, I teach undergrad systems programming in C, I use Python at the startup, and I use a mix of C/C++/Rust in research.)
I would personally much prefer to use Rust for code exposed to external untrusted input than to use C. I have substantially more confidence that I would not add exploitable bugs given the same time budget.
My favorite framing for this is that rust front loads all the pain.
C and C++ are incredibly subtle languages. But you can get a lot of code written before you run into certain foot guns in C and C++. This gives those language a more enjoyable on-ramp for beginners.
In comparison, rust is a wall. The compiler just won’t compile your code at all if you do anything wrong. This makes the act of learning rust much more painful. But once you’ve learned rust, it’s a much smoother experience. There’s far fewer ways for your programs to surprise you at runtime.
At least the Rust compiler gives pretty good advice on what is going wrong. And for complete beginners, agentic AI can soften the pain a lot if used correctly. By used correctly I mean the following work flow:
1) Design in correspondence with AI. Let it criticise your ideas, give you suggestions on tools/libraries/techniques, and have concepts and syntax explained to you. Stay aware that these models are sycophantic yes-machines.
2) Implement yourself.
3) Debug in collaboration with AI. If you ask a question like "I'm getting [error], what are the most likely reasons for this happening?", you can save a lot of time finding the issue. Just make sure to also research why it is happening and how to solve it independently.
4) Let AI criticise your final result and let it offer suggestions on what to improve. Judge these critically yourself.
There is some worth in spending hours trying to fix a bug you don't understand, it builds resilience, helps you get familiar with a lot of language topics, and you probably won't make the same mistake again. But the above approach is a pretty good compromise of letting AI help where it excels, while still keeping enough control to actually learn something yourself.
I believe that Rust is the language benefiting the most from agentic AI, because the compiler is such a strong gate-keeper, and the documentation of almost all aspects of the language is comprehensive and clear. The biggest pain points of Rust are also reduced by AI: Front-loaded learning curve is softened, refactoring is something gen AI is actually decent at, and long compile times can be spent productively by already planning out the next steps.
> I believe that Rust is the language benefiting the most from agentic AI
Except in my experience, chatgpt and claude both struggle to write rust code that compiles correctly. Chatgpt is pretty good at complex tasks in typescript like "Write a simple snake game using (web framework x). It should have features X and Y". Its can be surprisingly good at some complex problems like that.
If you try the same in rust, it often fails. I've also had plenty of situations where I've had some complex borrowing error in rust code, and chatgpt just can't figure it out. It goes in loops. "Oh I see the problem. Sure, this should fix it ..." except the "fixed code" fails in just the same way.
I'm not sure why. Maybe there's just not enough rust code in the training set for chatgpt to figure it out. But rust is definitely a weakness of the current generation of models.
The pain will always remain when refactoring or changing code, with modifications cascading in the function and type definitions.
If a language is hard to write at first, it’s always hard to write. The saving grace of C++ is that one mustn’t use the overcomplicated functional aspects, template meta-programming, etc. Through some amazing circumstances, all of the above (or their equivalents) + async is exactly what idiomatic Rust code has become.
Inside Rust there is a not so ugly language that is struggling to come to light and it is being blocked at every step.
> If a language is hard to write at first, it’s always hard to write.
That seems obviously false. Most fancy programming languages are difficult to write at first, C++ included. But they all get easier over time.
Rust got way easier to write over time for me. I'm soooo much more productive in it now compared to when I started. Does C++ not get easier to write over time too?
How about you teach some of those students to Bootstrap a Linux cross compile both with and without rust then come back and answer this reply again
This seems unrelated to the topic at hand, which is whether Rust results in a net improvement in the time required to deliver (systems) software that meets a particular set of requirements and those requirements include resilience to handling untrusted inputs.
Of course adding an additional set of tooling complicates an environment. I'm sure that was the case for Google in adding Rust to Android as well. And yet - it seems to have proved worth it. And I suspect that in the long term it will prove likewise for Linux, because Linux shares the same requirements and has a similar threat model it needs to guard against.
The reason it is relevant is this:
"This seems unrelated to the topic at hand, which is whether Rust results in a net improvement in the time required to deliver (systems) software"
A lot of your argument assumes that you've already have an entire working build environment readily available. If this is not the case, what used to be a fairly straightforward C compile now becomes an massive ordeal
Is it harder to learn than C? For sure it is a bit harder to get started. But is it also harder to learn than writing proper C(++?) with the same amount of quality in terms of lack of bugs ?
C has plenty of high quality linters like ClangTidy that can teach junior and intermediate developers what not to do. Granted, even with linters, C projects typically have more vulnerabilities than Rust projects, but C has fewer concepts a developer must know to produce working code. For example, to implement a self-balancing binary tree in Rust, you need to first understand reference counting, `RefCell`, and ownership semantics. In C, you just need to know what a struct is and what a pointer is.
> In C, you just need to know what a struct is and what a pointer is.
Suppose we have a team of experts busily analyzing every single state of the code. They are reading/valgrinding/fuzzing/etc.-- in real time as the intermediate developer writes code.
Each time the developer tries to compile, the team quickly votes either to a) remain silent and leave the dev alone, or b) stop compilation because someone thinks they've discovered an invalid read/write or some other big no-no that the compiler will not catch (but the Rust compiler would catch).
If they choose b, the experts stop for a bit and discuss the clearest way to communicate the hidden bug. Then they have a quick conversation with the intermediate developer. Suggestions are made, and the whole process repeats.
Is this process substantially faster than just learning Rust?
Edit: clarification
> to implement a self-balancing binary tree in Rust, you need to first understand reference counting, `RefCell`, and ownership semantics. In C, you just need to know what a struct is and what a pointer is.
It is misguided to say that recursive data structures should be easy to write. They are difficult to reason about and the Rust compiler is right to point this out. CS is not engineering, you should be writing those trees with a pencil on a piece of paper as Dijkstra intended, not in C.
How often do you DIY a self-balancing binary tree? Sure I wrote a few in college, but in the real world you are almost always just using an existing library. Optimizing for edge cases like that doesn't make sense.
And is your hand-written C implementation going to be safe and correct. You didn't mention any kind of locking or atomic operation, so is it going to unexpectedly break in a multithreaded environment?
The concepts of Rust are definitely more complicated, but in practice it just means that C makes it easier to shoot yourself in the foot. It's easy, but is that really the most important thing here?
This assumes a perfect student who is not going to make any mistakes while writing their code, not even a typo. If an error is introduced, things become much harder on the C side. The compiler may miss it. It could manifest as a bug that only occurs in certain non-obvious circumstances, and manifests erratically (e.g. an out-of-bounds write clobbering data of a node next to it in the heap). Rust would most likely just not allow such a bug to be introduced.
In the same vein, driving on a modern busy road requires you to know about lanes, speed limits, various signs, traffic lights, rules of turning and merging, etc, etc. A road without all of that, where a steering wheel plus two pedals suffice, of course still allows you to drive, and drive fast, but it requires much more attention from a driver; many driver's mistakes are noticed later, and lead to more dangerous accidents.
> For example, to implement a self-balancing binary tree in Rust, you need to first understand reference counting, `RefCell`, and ownership semantics.
This assumes a self-balancing binary tree must have nodes with parent pointers. Without those you don't need reference counting and without that you don't need `RefCell` either.
How often do you find yourself implementing self-balancing trees in either C or Rust?
once in university
> Because it's not a silver bullet.
It does look like a silver bullet, actually. In the context of software engineering, "silver bullet" inevitably leads to Fred Brooks:
'"No Silver Bullet—Essence and Accident in Software Engineering" is a widely discussed paper on software engineering written by Turing Award winner Fred Brooks in 1986. Brooks argues that "there is no single development, in either technology or management technique, which by itself promises even one order of magnitude [tenfold] improvement within a decade in productivity, in reliability, in simplicity."
Reducing memory-safety vulnerabilities by 5000x compared to the prior approach is not just a silver bullet, it's an arsenal of silver bullets.
> the compilation time for code with equivalent semantics is an order of magnitude greater
The time it takes to write and run the comprehensive tests for C and Zig code to demonstrate anything even approximately in the ballpark of what Rust gives you for free is a multiple orders of magnitude greater than whatever time you spent waiting for the Rust compiler. Why care about the time it takes to compile trivially incorrect code, rather than caring about the total time it takes to produce reliable software, which is demonstrably lower for memory-safe languages like Rust?*
Who said that anyone is absolved of the responsibility to write tests for business logic when using Rust? I struggle to see anything in the comment you replied to that is anywhere close to claiming this
> paper reports three orders of magnitude less issues, cites evidence
> baseless claims
Not even downvoting.
To me, those sound like meaningful pros and cons, not a reason to completely dismiss it.
And the evidence is: Google's PR piece.
>"There are certain places on the internet where any mention of rewriting in Rust is met with scorn and ire."
Nah. For me it induces vomit. Any time the vulnerability is mentioned a horde will arrive promptly and sing "rewrite the ... " in Rust.
For fuck's sake. We got it. We know it is mostly memory safe with bunch of other protections baked in. So go and rewrite it and then try to sell it to your customer and have them pay for this job. If you succeed - congrats, good for you and for the "victims". If not - stop nagging people who have other things to worry about.
One who has tried the language and hates it can completely dismiss it. Memory issues can be fixed in ways that don't involve rewriting millions of lines of code and the ensuing chaos, feature degradation, retraining, etc. that goes with it.
Your reply isn't necessarily "polite" but your reaction and is entirely appropriate considering the headaches these people are bringing
It's not just the headaches, it's the smugness with which they tell us that C and C++ are too bad to be used and the only solution is to throw it all away and start over. I've seen some smug language evangelists before, but Rust has the worst. It's just too much work to rewrite everything and retrain everyone, for the sake of maybe preventing this one category of error. Calling memory errors "unsafe" is also a bit of a stretch.
It's one class of bugs. This is not enough information to make a technical decision. Logic bugs still exist. It's not clear if these are easier or harder to create in Rust. There is some existing evidence to suggest that object oriented programs make it easier to create logical errors and emergent bugs.
So, that's why I completely dismiss it, it fraudulently attempts to champion Rust as an obvious replacement of anything. For those who think this has something to do with Rust specifically, no, we've held these reservations for promised replacement languages for decades now. There is no evidence Rust's borrow checker alone has overcome enough of the problems of any previous example.
A short criticism of Rust is, for a supposed systems language replacement, they let far too many features and first party magic (cargo) into the language.
70% of bugs in a large mature c++ code base come from memory safety bugs. Yes it's not the only type of bug, but it sure is the majority. Other types of logic bugs are also easier to avoid in rust because it's type system is quite powerful. Rust enums make it easier to associate state with specific states, option types actually force you to check if they are valid, result types force you to check for errors, etc. Anyone who's actually migrated a code base from c++ to rust should be able to attest to the benefits.
> 70% of bugs in a large mature c++ code base come from memory safety bugs.
Are 100% of those exploitable? This single ended statistic is simply not useful.
> Other types of logic bugs are also easier to avoid in rust because it's type system is quite powerful.
You have proof of this?
> Anyone who's actually migrated a code base from c++ to rust should be able to attest to the benefits.
That's not how these measurements work. In particular, modern C++ has many of the same advantages you just cited, so this claim is dubious in two ways.
And you've entirely failed to address the largess of Rust, which, again, for a "systems language" is entirely mismatched.
> And you've entirely failed to address the largess of Rust, which, again, for a "systems language" is entirely mismatched.
I'm not entirely sure where this idea even comes from. Why would it be desirable for a systems programming language to be sparse on features?
Bugs don't need to be exploitable to be a problem. The program is incorrect and invariably will fail to act correctly due to those bugs.
Modern c++ has none of the advantages I mentioned. std::variant is cumbersome to use and you cannot easily match on it exhaustively, std::optional and std::expected have UB if you look at the stored types without checking if it's valid first. I love c++ and still use it daily, but I also know I write more bugs when I use it compared to rust. I don't have hard proof that rust is less prone to other types of bugs but wouldn't be surprised if someone produces a blog post with evidence. If you're trying to make judgement without experience, then that's simply foolish.
I'm not sure why you would point to "bigness" of the language as a problem when you're fine with considering c++ a systems language. If you don't like cargo, you don't have to use it. There are other build tools that can integrate with the rust toolchain the same way you would do so with c++. If some libraries are not suitable, don't use them. There are plenty of c++ libraries like boost which I avoid because they don't work well for me too. Consider the way in which The Linux kernel is using rust the language. The key to whether a systems programming is useful is whether I can meet the constraints the software must run in, typically in terms of binary size, memory usage, and runtime performance. Rust delivers here and can be used anywhere c/c++ have found success.
> Are 100% of those exploitable? This single ended statistic is simply not useful.
Some more links/statistics (including on 0-days exploited in the wild) here: https://www.memorysafety.org/docs/memory-safety/#how-common-...
> You have proof of this?
One class of non-memory-safety bugs that safe Rust entirely prevents is data races: https://doc.rust-lang.org/nomicon/races.html
> That's not how these measurements work. In particular, modern C++ has many of the same advantages you just cited, so this claim is dubious in two ways.
Are the advantages in those cases (option/result) properly realized when it's just piled on top as something you could do?
I still have to be wary that, for instance, std::strchr could return a nullptr which I might pass on without handling. Plus even with std::optional<T> it's on me to remember to check val.has_value(), else it's undefined behavior to access.
Whereas in Rust, s.find() must return an Option<usize> because there's no null for it to return otherwise, and accessing the inner value (`match`, `if let`, ...) requires handling the Option::None case.
> And you've entirely failed to address the largess of Rust, which, again, for a "systems language" is entirely mismatched.
Large compared to C or Zig, but not compared to C++.
Generally speaking, the purpose of a program is not to minimize the number of memory safety bugs. All other things being equal, yes, having fewer memory safety bugs is better than having more. But perhaps you're trading legible bugs for illegible bugs? The rust implementation is most likely going to be more complex than the c implementation (which is fair since it almost eliminated a whole class of bugs), and in that complexity there is extra room for non-memory safety related bugs.
There's probably also 500x more people who know c to a given level then know rust to a given level.
If we have an analyzer that can find memory safety bugs in C, we could also just put that in the CI pipeline, or as a pre-submit hook before you're allowed to add code to a code base.
This idea that if Rust doesn't have all those memory safety bugs it must somehow have loads of other bugs we haven't discovered reminds me of Americans insisting that countries which don't have their lousy gun safety problems must have the same effects by some other means they haven't detected - Like, OK England doesn't have lots of gun murders like America, but surely loads of English people are just dropping dead because someone threw a yoghurt at them, or used harsh language, and we just missed them off our statistics ?
No man, it is possible to just do better, and this is an example of just doing better. The Rust is just better software. We can and should learn from this sort of thing, not insist that better is impossible and the evidence suggesting otherwise must be a mirage.
I don't see the connection, did you reply in the wrong place?
You're not fully understanding the issue with memory safety. When you write C or C++, you're promising that you won't violate memory safety at all. That's just a basic requirement of what it means to write in those languages.
The graph about reverted code also addresses the "illegible bugs" argument.
As for an analyzer, that's what ASAN is. I hope I don't need to explain why that's not a universal solution (even though everyone should be using it).
> You're not fully understanding the issue with memory safety. When you write C or C++, you're promising that you won't violate memory safety at all.
The post you reply to does not indicate a misunderstanding of memory safety at all. .
The comment I'm responding to implicitly assumes memory safety violations are like other bugs where that it's meaningful to speak of programs being more or less correct depending on the number of issues. What I'm emphasizing is that code with safety violations, strictly speaking, isn't C/C++ at all. It's more like parsing paint splatters as perl [0]. You might get something resembling what you want if you're lucky, but you also might not depending on how the compiler feels that day.
Let's use an example: https://godbolt.org/z/TP6n4481j
The code shows main immediately calling a nullptr. What the compiler generates is a program that calls unreachable() instead. These are two different programs. If memory safety is "just" a bug, this would be a miscompilation. It's not a miscompilation though, because what I've given the compiler is something that resembles C++, but is actually some similar language where null dereferences are meaningful. The compiler only knows about C++ though and C++ doesn't have nullptr dereferences, so it assumes I haven't done that. Instead it generates a program corresponding to an execution trace that is valid C++, even if it can't see the call to NeverUsed(). If you use -O0, you get the segfault as expected.
A single instance of memory unsafety (or other UB) can take your program arbitrarily far from "correct". All other things being equal, a program with 1 violation might be just as incorrect as a program with 100. I could add a hundred more lines of safety violations after Do() without changing the compiled behavior. You don't even need to execute the unsafety to have "spooky action at a distance" cause that change.
[0] https://web.archive.org/web/20190406194101/http://colinm.org...
Finally someone who actually gets it.
Many Rust proponents are experienced C and C++ developers who have dealt with this situation for decades. Given the language, it's understandable that compilers make the choices that they do. It's also understandable that programmers find it unreasonably difficult to reason about code written in such a language.
> What I'm emphasizing is that code with safety violations, strictly speaking, isn't C/C++ at all.
This isn't really correct and many programming language standards (including that of C and C++) don't support this view. Many language standards define a notion of conformance. Strictly conforming programs aren't allowed to invoke behaviors that which are undefined[1].
Conforming programs do not have this requirement and basically any non-trivial C and C++ programs are written to this rather than the notion of "strictly conforming".
Most non-trivial programs are not strictly conforming (including some C compilers themselves), generally because restricting the set of targets to something smaller than "any possible C implementation" is useful.
It is perfectly legal (and very desirable in cases where the standards fall short of usefulness) for a C compiler to define undefined behavior. What you compiled is still a C program, just one that isn't portable across the entire potential set of implementations.
[1]: Or unspecified or implementation-defined, for that matter, but this part tends to get left out of discussions.
Chances are, a Rust implementation of certain things may be simpler than C implementation. C is a low-level language, so you have to do more housekeeping, and express things obliquely, via implementation, vs expressing things more declaratively in Rust.
Being simpler is not a given though.
"Knowing C" as being able to read and understand what's happening is quite separate from "knowing C" as being able to write it competently. Same thing with Rust: an algorithm written in rust is far from impenetrable for a non-expert, and even someone who sees Rust the first time but has enough experience with other languages.
The idea that people occasionally throw around that C is more 'simple' and less 'complex' than C++ or Rust and therefore it leads to more maintainable or easy to understand code is, IMO, completely bogus.
C is not simple, it is inept. There are so, so many bargain-bin features and capabilities that it just cannot do that it ends up creating much MORE complex code, not less complex code.
I mean, just the pretense that simple tool = simple engineering isn't necessarily true. Building a home using an excavator and drills is fairly straight forward. You know what's complicated? Trying to build a home using only a screwdriver. Yeah. Good luck with that, you're gonna have to come up with some truly insane processes to make that work. Despite a screwdriver being so much more simple than an excavator.
Trivial example: you want to build a container that can hold data of different types and perform generic operations on them.
C++ and Rust? Easy. Templates and generics. C? Up until a few years ago, your options were: 1. copy and paste (awful) or 2. use void * (also awful).
Copy and paste means your implementations will diverge and you just artificially multiplied your maintenance burden and complexity. And void pointer completely throws away any semblance of type safety, forces you to write stupid code that's way more complex than it needs to be, and, to top it off, is horrible for performance!
That's just one example, but there's so, so many when you look around C++ or Rust enough. And these are not rare things, to me. To me, these are everyday coding problems.
Anonymous functions? There's another one. Encapsulation? Just making not literally every piece of data universally mutable? Not possible in C. Trivial in C++ and Rust, and it makes your programs SO much easier to reason about.
> Just making not literally every piece of data universally mutable? Not possible in C. Trivial in C++ and Rust, and it makes your programs SO much easier to reason about.
And Rust is significantly better at this than C++ for the simple reason that mut is a modifier. I’ve lost track of how many times I’ve listened to Kate Gregory extol the virtues of const-ing all the things, but people still don’t systematically add it, and, as readers, we’re left wondering whether things actually need to be mutable, or the author forgot/didn’t know to add const-ness to their code. With Rust having opt-in mutability, you know for a fact that mutability was a deliberate choice (even if sometimes the only motivation was “make the compiler happy”).
> I’ve lost track of how many times I’ve listened to Kate Gregory extol the virtues of const-ing all the things, but people still don’t systematically add it
Adding const to _function-local_ variables only really matters when you "leak" a pointer or ref, whether mutable or const, to a function or variable the compiler can't optimize away:
std::size_t sz = 4096;
const std::size_t &szRef = sz;
some_opaque_func(szRef);
if (sz != 4096) std::abort(); // cannot be optimized away unless sz is const
as there is no way to know if something obtains a mutable ref to sz down the line.In other cases like RVO, adding const is actually detrimental as it prevents the move-constructor from being selected (likewise with the move assignment operator).
Rust _needs_ to have const by default due to its aliasing model ("only one mutable ref per object") and you can't have cheap bound checks without this. But that, too, is a tradeoff (some classes of programs are hard to code in Rust)
Pretty sure the std::abort() can't be optimized away if sz is mutable since it's legal for some_opaque_func() to cast away szRef's const and modify sz via that. sz itself needs to be const for the if statement to be removable as dead code.
https://cpp.godbolt.org/z/Pa3bMh9Ee shows that both GCC and Clang keep the abort when sz is not const. Add const and the abort goes away.
> The idea that people occasionally throw around that C is more 'simple' and less 'complex' than C++ or Rust and therefore it leads to more maintainable or easy to understand code is, IMO, completely bogus.
This, this, this.
C compilers are simple, but the C language is not, and let’s not even talk about C++.
I like to focus on the ways that C is actually quite complicated, especially the complications that directly provoke UB when you don't know about them. Integer promotion and strict aliasing are at the top of my list.
>Trivial example: you want to build a container that can hold data of different types and perform generic operations on them.
Do I?
I would simplify the problem to not need different types or generic operations.
Or if I really need generic operations, break them down to smaller operations so you don't need to take a bunch of type parameters everywhere.
For example containers, instead of having container<T>, have the container operations return an index or 'opcode', then the user applies that to their data. The container doesn't need to know about T, void pointers or sizes, just its own internal bookkeeping stuff.
That's valid, even sensible, but you have now left significant performance on the table.
To be honest I feel like "this code is easier to review and less likely to require rollbacks" is even more of a valuable take from this article, just in terms of "hey, don't you like it when things don't have to be rolled back?"
Security issues are like bad etc too, just we've heard the security spiel so many times at this point. I just think it's nicer to write most stuff in Rust.
Yes, Rust's strictness makes it a lot more maintainable. It is so much more common that changing the one thing you wanted to change results in a compiler error at every single other site you need to change, without having to look at other areas of the codebase at all, and all the tests pass on the first try.
Rust is truly a marvel of engineering. A breakthrough. Such a thing is so very rare in computer science.
I don't know much about how it got started. I'm curious how much of Rust's capabilities depend upon recent CS breakthroughs. Could we have made Rust in 1990?
The compiler is also relatively slow. Would Rust have been worth working with on 30+ year old hardware?
> Could we have made Rust in 1990?
No. Only massively oversimplifying, Rust could be described as a bunch of ideas pioneered among functional languages coming back to C++, the same way Java was a bunch of ideas from Lisp coming back to C. There is very little that's truly new in Rust, it's just mixing a bunch of features that were not often together before.
> The compiler is also relatively slow. Would Rust have been worth working with on 30+ year old hardware?
What makes Rust slow to compile is largely independent of what makes it unique. A lot of text has been written about this, but the again massively oversimplified version is that had the designers cared about compile times when the language was being designed and the compiler written, you could have something that's very similar to Rust but also very fast to compile.
> The compiler is also relatively slow. Would Rust have been worth working with on 30+ year old hardware?
As I understand it, a lot of the slowness of the rust compiler comes about from llvm. And how rust and llvm interoperate. Rustc creates and sends gigabytes of stuff to llvm - which passes all of that to its optimizer. If you skip all that work - for example by running cargo check - the compiler is an order of magnitude faster.
If rust were invented in the 90s, it wouldn’t have used llvm. Rust could still have been implemented, and we’d probably have a much faster compiler as a result. But it would have missed out on all the benefits of llvm too. It would have needed its own backend to be written - which would have been more work. And the compiler probably wouldn’t have been as good at low level optimisations. And it probably wouldn’t have out of the box support for so many target platforms. At least, not from day 1.
The obvious(?) question is why it sends gigabytes of stuff to llvm and if that can't be reduced somehow.
> The obvious(?) question is why it sends gigabytes of stuff to llvm
IIRC it's a combination of technical debt from earlier in Rust's life (it's easier to generate naive LLVM IR and let LLVM's optimizer do the heavy lifting of chewing through that) and how Rust implements generics via monomorphization
> and if that can't be reduced somehow.
I believe the technical debt bit can be (and is being!) reduced by implementing optimizations and better IR generation in rustc itself. As for the monomorphization strategy, I thought I remembered reading something about how Rust technically allows for generics to be implemented via non-monomorphization strategies like type erasure/dynamic dispatch, but I can't seem to find that post/article/whatever it was now so I'm not sure I'm not making it up. That being said, there are patterns to reduce the amount of code generated (e.g., generic facade that forwards to a non-generic implementation), but those need to be manually implemented at the moment and I don't think there's significant work towards automating that at the moment.
I think the answer is probably that Rust was possible in the 1980s and 1990s, but such a thing just wasn't practical.
Rust is notoriously compiler-intensive. That wouldn't have been tolerated in the early PC era. When you needed fast compilers that "worked on my machine" and should work on yours. Ship it.
It wasn't really possible. We had neither the PL techniques nor the computational power to make something like Rust work at the time. All the answers people are throwing around showing it would have been possible rely on a garbage collector and require a runtime, or have many other unacceptable compromises (e.g. no use after free because you aren't allowed to free).
> Could we have made Rust in 1990?
We did, it was called OCaml. If we'd had any sense we'd've rewritten all our systems code in it. But since C had bigger numbers on microbenchmarks, no-one cared.
One of Rust’s biggest and best features is its trait system inspired by Haskell’s type classes. It is the right abstraction for most use cases that in 1990 were implemented by OOP and inheritance. Now the basics of type classes were invented by Wadler in 1988, but certain more advanced features (type families) were only invented in 2005. You mention OCaml but that’s only a small part of Rust’s type system.
So the answer is no, because humans’ collective expertise of programming language theory simply isn’t enough in 1990, unless Rust developers independently invented such features instead of copying them from GHC Haskell.
> One of Rust’s biggest and best features is its trait system inspired by Haskell’s type classes. It is the right abstraction for most use cases that in 1990 were implemented by OOP and inheritance. Now the basics of type classes were invented by Wadler in 1988, but certain more advanced features (type families) were only invented in 2005. You mention OCaml but that’s only a small part of Rust’s type system.
I submit that those advanced features are at most a tiny fraction of why projects like OP are seeing benefits from moving to Rust. E.g. I wouldn't be at all surprised if this Rust on Android project isn't using type families at all, or is using them only in an incidental way that could be replaced without significantly compromising the benefits.
Any Rust code longer than ~20 lines uses Rust iterators which use type families. The Iterator trait in Rust has an associated type called Item. This is the innovation here. Classic type classes can only contain functions not types. It was in 2005 that a paper was written to show how having a type inside a type class makes sense, including how it can be type checked (via entailment of type class predicates with type equality), type inferred (by changing the standard HM system to return partial type equality constraints in addition to substitutions).
Now if Rust did not have such language features maybe it would have implemented iterators very differently. Current Rust iterators are similar to Java iterators, and in Java, iterators themselves have a type parameter, rather than having an associated type inside the iterator trait.
All of the memory safety stuff is independent of the trait system, to the best of my knowledge, but the data race protection is implemented through the Send and Sync traits. I'm unsure if there is an obvious alternative approach to this same feature, but I think it may be one innovation that is still novel to Rust and would not have existed in earlier decades.
Rust also has the bigger numbers on microbenchmarks, that's why people care about it.
Yes, exactly. It's tragic that the only way programming culture ever improves is when a language comes out that's better for writing software in and happens to also have bigger numbers on microbenchmarks.
I think you're generalizing a bit too much. Rust targetted the audience that wanted big numbers on microbenchmarks, but not all languages do. Typescript for example has no performance advantage against Javascript, but it became very popular due to the better dev experience. Kotlin is another example where that mattered a lot.
Yeah, I am blown away. Assuming that these stats are true/verifiable, this spells real doom for C++. What is the point of C++ in 2025 except to maintain a large, existing source code base? Else, you should be doing everything that you used to do in C++ in Rust.
> What is the point of C++ in 2025 except to maintain a large, existing source code base?
Half of useful things to do are impossible or plain cumbersome to write in rust given the semantics and constraints of the borrow checker. Try to write self referential structures in rust and you'll have a more nuanced opinion.
That’s entirely the point, though. Rust compiler is of the opinion that recursive data types are hard and I don’t think it can be reasonably argued that this opinion is incorrect.
Feel free to use unsafe {} when you need it, though.
I’ve written self-referential structures in Rust with the ouroboros and self_cell crates. It was fine, and I got way more guarantees I got it right compared to C++.
That's also a reason why Google is researching with Carbon, they have for sure a hunger to migrate away from C++.
Note that N=1 for the memory safety vulnerabilities they had with Rust, so the error of the estimated average number of vulnerabilities per LOC is quite large.
Yes. I would make a guess of 10 or less memory-safety vulnerabilities per MLOC, which is still a hundredfold reduction.
Your best guess is that the true rate is 20x higher than the observed rate? This seems unlikely to me given the number of samples (outside of systematic biases towards certain types of memory safety bugs that probably apply to C++ code too). 10 per hundred MLOC is closer to what I would have guessed too, but that is because I've historically been very conservative with my assumptions about the memory unsafety rate per unsafe LOC being similar to that of C++. The evidence here suggests that the true rate is probably much lower than that.
I'm making a conservative guess, which is why I said 10 or less (10 or fewer??). So the improvement is at least a hundredfold. I might say 5 or less instead. I think the exact rate is not so important; either way, it's clear that Rust is a boon.
Those are orange vs apple, just like the rate of rollbacks
They compare something new, which rewrite existing stuff (not only but still) with some decades-years-old cruft
In they new code, they know what they want
They can also start with state-of-the-art unit testing that may not exist in the early 2000
So .. yeah, those numbers ..
That rust is saner than c++ is a given anyway :)
Isn't that a lot though, that means 1 memory safety vulnerability per 1000 lines of code, that seems hard to believe.
Take a look at the examples in this post: https://www.microsoft.com/en-us/msrc/blog/2019/07/we-need-a-...
Large C++ codebases have the same problems that large codebases have in any language: too many abstractions, inconsistent ways of doing things, layers of legacy. It comes with the job. The difference is that in C/C++, hard-to-read code also means hard-to-guess pointer lifetimes.
If the C++ code I worked on looked like that[1] and was actually C with classes, then I’d be switching to Rust too. For Google and Microsoft it probably makes sense to rewrite Windows and Android in Rust. They have huge amounts of legacy code and everybody’s attacking them.
It doesn’t follow that anyone else, or the majority has to follow then. But that’s predictably exactly what veteran rustafarians are arguing in many comments in this thread.
[1] Pointers getting passed all over the place, direct indexing into arrays or pointers, C-style casts, static casts. That (PVOID)(UINT_PTR) with offsetting and then copying is ridiculous.
It's not _that_ hard to believe if you start spraying smart pointers everywhere.
Per million lines of code.
I fear Androids problems with Google as steward has other significant problems than memory safety.
To a degree that users might want to even exploit such flaws to unlock their phones.
Aside from that. Sure, the constraints of Rust do solve these kinds of problems.
Absolutely crazy.. I never expected that the difference would be so drastic.
They found a memory safety bug in their Rust code and assumed it was the only memory safety bug in their Rust codebase. And then they compared it to the historical average in C++ code that's been around for almost two decades in production. I can't be the only one here who sees how biased this comparison is right?
Rather, they found one memory safety bug in their Rust codebase, and measured it against the legions of memory safety bugs they found in their C++ codebase. In neither case are they measuring against bugs not found, so no, it's not biased.
Except it's not an apples-to-apples comparison. The C++ code has been around a lot longer, and a lot of it was written with older versions of C++ which didn't have modern safety features. I'm sure there is a bunch of new/delete in their codebase still. And I'm sure they're actively looking for memory safety issues in C++, and probably not so hard (if at all) with Rust.
> The C++ code has been around a lot longer
They made an earlier report where they found out that older C/C++ code has actually a lot less new vulnerabilities compared to new code, so I guess here they are comparing to new C/C++ code to get the higher ratio, meaning the comparison should actually be apples-to-apples.
Is it a usual thing you do that when you're given data about a literal thousandfold improvement, in a context where there are well-understood theoretical and practical reasons why you might have expected to see such an improvement, you make up reasons why it is actually not an improvement at all, without either investigating to see whether those reasons are actually true or demonstrating that even if they were true, they could possibly explain more than a tiny fraction of the improvement?
I usually am skeptical about a literal thousandfold improvement, yes. And I'm not saying it's impossible, but rather that the data and the way it's presented has inherent biases. Its on the people making grandiose claims to prove them.
The fact that the safe subset of Rust can indeed be made safe, and that unsafe code can be encapsulated like this, have already been formally verified. This is an empirical demonstration that matches these formal results across a large company with a large amount of code (technically, it exceeds them, but this is only under the assumption that memory safety issues per line of unsafe Rust are identical to memory safety issues per line of C, which is really an unwarranted simplifying assumption).
Do you really believe that "they're not actively looking for memory safety issues in Rust" is (1) true (at least outside of Google, there is actually a ton of security work done specifically targeting just the unsafe blocks, since those are obviously where the memory safety issues lie) or (2) could possibly be responsible for a literal thousandfold reduction in memory safety issues? Remember that the Rust code is often integrated with C++ code--there is not necessarily a way to just test the C++ part even if you wanted to. Additionally, Google has explicitly prioritized code that interacts with untrusted data (like parsers and networking code) meaning it's likely to be easier to fuzz most of this Rust code than most new C++ code.
Also remember that, again, there are mechanized proofs of memory safety for a large subset of the safe portion of Rust, which constitutes 96% of the code under consideration here. The rate of memory safety bugs would have to be 25x as high per LOC in unsafe Rust code as in C code for the number of vulnerabilities to match. It would be far more shocking if we didn't see a dramatic reduction. Google is empirically demonstrating that the observed memory safety bugs per line of unsafe Rust is actually far lower than per line of C, but my point is that even if you think that is the result of bias or them not applying the same scrutiny to Rust code (something that is certainly not true of Rust vs. C code in the wild), the effect of this underrepresentation cannot possibly explain most of the reduction they observe.
Google deployed numerous state of the art mitigations prior to adopting Rust and still found that 70% of their CVEs were due to memory safety issues--your assertion that they are engaged in motivated reasoning and just wanted Rust to work out is pretty ill-founded. In fact when I worked for Google prior to Rust's release, they were strongly averse towards adopting any new language and believed that good engineering practices, automation, and a rigorous review process always outweighed the benefits of adopting a new language past their core ones, whatever its purported reliability or performance benefits. Security researchers are highly skeptical as a rule of these sorts of claims and have a lot of say at Google. They themselves changed their minds based on this sort of internal evidence.
I agree that skepticism is warranted, because we are constantly being sold things by industry. At a certain point, though, when the effect size is massive and persistent and the mechanism extremely clear, that skepticism (not in general, but of a particular claim) becomes the unscientific position. We are well past that point with Rust wrt memory safety.
Because I don't blindly accept bad science? It's more like others are sure that this data confirms their biases.
If you want something more comparable, they estimate that only 5% of their Rust code is within unsafe blocks. That makes only 5% of the code with potential for memory safety issues: that's already a 20x improvement. Let's make it 10x because unsafe blocks tend to be trickier, but you still get a lot of Rust guarantees.
The thing is with Rust, you know where to look for memory safety issues: the unsafe blocks. C and C++? GLHF that's your whole codebase. As they mentioned, you don't opt-out of all of Rust guarantees by going the unsafe route. Of course you can ditch them, but that'll be hugely visible during code review. Overall, you can be much more confident saying "yup there's no bug there" in Rust than in C or C++.
Even if they are off by a factor of 100 it's a huge win and the point stands.
It's fair to point this out and worth the mention. Still, I'd like to think that the engineers behind this can at least gauge the benefit of this endeavor with some accuracy despite the discrepancy in available data, and stating the data that is available only makes sense.
and they found 1000 memory safety bugs and assumed it was the only 1000 memory safety bugs in that code which have been in production for 2 decades. How naive can they be?
Even so, relatively speaking if C is not 50,000x worse, it must be at least 2000x worse.
Further up they refer to Android C/C++ code, not C/C++ in general:
"We adopted Rust for its security and are seeing a 1000x reduction in memory safety vulnerability density compared to Android’s C and C++ code."
Which means they had a pretty poor code base. If they had spent more time on engineering and less time on features that are canceled after 12 months anyway, they could have written better C/C++.
I peruse Android system code at work and their C++ code base is not designed for safety. It’s just typical C++ code as any large company would write it.
And for a large juicy target like Android, that won’t be good enough to stay ahead of the attackers long term.
Of course, tools like Fil-C or hardware-based security might make Rust vs. C or C++ moot.
Edit: your comment makes a good point. Shame that trigger-happy (c)rustaceans are downvoting everything in sight which is not praising this PR piece disguised as a technical blogpost.
While crashing is better than exploitable behavior, catching bugs at compile time is even better. Neither hardware based strategies nor filc actually find bugs at compile time. Also the conversation with respect to security isn't about migrating old code, but what to use for new code that you write.
I will note that developers also feel more productive in rust. That's why they migrate existing things over to it even when it may not be beneficial for security.
Rust has been such a "pain" to learn - at least compared to other, more straight-forward languages. But boy does it feel good when you know that after a few back and forths with the compiler, the code compiles and you know, there is not much that is going to go wrong anymore.
Of course, I am exaggerating a bit - and I am not even that experienced with Rust.
But after coding with Ruby, JS/TS and Python - it feels refreshing to know that as long as your code compiles, it probably is 80-90% there.
And it is fast, too.
That’s my favorite feature. Rust turns runtime errors into compile time errors.
All that fighting with the compiler is just fixing runtime bugs you didn’t realize were there.
Rust is the most defect-free language I have ever used.
I'd wager my production Rust code has 100x fewer errors than comparable Javascript, Python, or even Java code.
The way Result<T,E>, Option<T>, match, if let, `?`, and the rest of the error handling and type system operate, it's very difficult to write incorrect code.
The language's design objective was to make it hard to write bugs. I'd say it succeeded with flying colors.
Now try an actual functional programming language. I like Rust too but those features all come from FP, and FP languages have even more features like that that Rust doesn't yet or can't have.
> Rust has been such a "pain" to learn - at least compared to other, more straight-forward languages. But boy does it feel good when you know that after a few back and forths with the compiler, the code compiles and you know, there is not much that is going to go wrong anymore.
I found that at some point, the rust way kinda took over in my head, and I stopped fighting with the compiler and started working with the compiler.
Rust is easy to learn. I've done it 4 or 5 times.
I'm interested in what scenarios you don't get this same feeling when writing TS code? I of course agree with Ruby, JS, and Python.
One big source of bugs in TS is structural sharing. Like, imagine you have some complex object that needs to be accessed from multiple places. The obvious, high performance way to share that object is to just pass around references wherever you need them. But this is dangerous. It’s easy to later forget that the object is shared, and mutate it in one place without considering the implications for other parts of your code.
I’ve made this mistake in TS more times than I’d like to admit. It gives rise to some bugs that are very tricky to track down. The obvious ways to avoid this bug are by making everything deeply immutable. Or by cloning instead of sharing. Both of these options aren’t well supported by the language. And they can both be very expensive from a performance pov. I don’t want to pay that cost when it’s not necessary.
Typescript is pretty good. But it’s very normal for a TS program to type check but still contain bugs. In my experience, far fewer bugs slip past the rust compiler.
Appreciate it, that makes a lot of sense. I feel like I've been trained to favor immutability so much in every language that I sometimes forget about these things.
Yes, immutability is great for safety. But the copies you have to make to keep everything immutable extracts a price in copies and garbage collection.
Rust is advertised as having fearless concurrency. That's true, but not that important as concurrency is not that common. What's important to everyday programming is Rust provides fearless mutability. The fearless concurrency you get with that is just a bonus.
Fearless mutability provides Rust the same safety as a functional language in a without the speed or space cost. IMO, it's Rust's true secret sauce.
Yea this seems like a super power I thought only functional languages had. I have to make time to learn some Rust
Similar. I mostly design my code around something like pipe and lifetime. The longer something needs to live the closer it is to the start of the program. If I need to mutate it, I take care that the actual mutation happens in one place, so I can differentiate between read and write access. For anything else, I clone and I update. It may not be efficient and you need to track memory usage, but logic is way far simple.
Not parent comment, but TS is generally safe if you have types correct at system borders, but very scary when you don't. Some of the most impactful bugs I've seen are because a type for an HTTP call did not match the structure of real data.
Also, many built in functions do not have sufficient typesafey like Object.entries() for instance
That is an issue with how TS works, but it can be significantly improved upon by using a library to verify the structure of deserialized data. zod is one example, or you could use protobufs. Fundamentally, this is an issue with any programming language. But having your base "struct"-like type be a hashmap leads to more mistakes as it will accept any keys and any values.
I disagree that this is an issue in every language - the problem is that in other languages the validation against some schema is more or less required for unmarshalling, and it's optional in TS.
Seeing a deserialization error immediately clues you in that your borders are not safe. Contrast that with TypeScript, where this kind of issue can lead to an insidious downstream runtime issue that might seem completely unrelated. This second scenario is very rare in other languages.
I don't know Rust, and I'm genuinely curious: How does it improve over that problem?
When you call a REST API (or SQL query for that matter), how does it ensure that the data coming back matches the types?
TS allows you to do parse the JSON, cast it into your target type, done (hiding correctness bugs, unless using runtime verification of the object shape, see sibling comment). Does Rust enforce this?
It validates the object shape at runtime, much like you can do in Typescript with a library like Zod. The key difference in this case is that Rust makes it scary to not validate data while Typescript will gladly let you YOLO it and blow your legs off, even in strict mode.
Okay I see, that's a nice secure-by-default point, whereas TS is arguably not secure-by-default.
It’s not. And trying to just be a transformation of the source to JS without its own standard library (mostly, some old stuff doesn’t follow this) means it really isn’t possible with just TS alone.
That’s OK with me. I use TS because I like it and hate the total lack of safety in JS. I have to use JS on the web, so TS it is.
If I don’t need it to run on a webpage, I wouldn’t be writing it in TS. I like other languages more overall.
The worst offender is toString which has different types between objects and is everywhere by default.
If you type correctly at border of your system, then TS will be very close to a formal verification of your code. This won't catch all bugs, but even broad categories for you data is helpful. If you know your input is a non-null string. Then it will warn you of every non string usage. It won't catch whether it's a name or an email, but knowing someone tries to divide it by zero is helpful.
It's a lot more effort, but branded types for conceptual differences can bridge that last gap
Typescript doesn't even support notions like "unsigned integer". It is not a serious attempt at type-safety; its main claim to fame is "better than raw Javascript" which is not saying much.
If you need to run in a JS environment it’s a hell of a lot. It’s a FANTASTIC improvement.
I wouldn’t use it server side or for a client application that doesn’t run in a web browser. That’s not its place, for me.
But I will 100% reach for it every time if I need to run in a JavaScript environment.
TS actually works very well as an HTTP server, I’d say better than typed Python. Plain JS… a minute of silence, please.
I’ve never been a JS on the server person, I was used to other languages when that was developed.
Well I think I would prefer python, but simply because it’s “more traditional“ and I realize that’s specious reasoning, I prefer to use strongly typed languages whenever possible.
I would generally reach for Java since it’s the language I’m most proficient in due to my career. There’s also Go, which I played with long ago, or maybe I’d try Rust.
This is only for anything important. If I was just toying with something locally I’d probably do whatever was fastest. In that case Python or JS might be my choice for a very tiny script.
Note that Google still doesn't have official support for using Rust in Android userspace, though.
Despite all pluses on the blog, NDK only supports C and C++ tooling, same on Android Studio, and it is up to the community to do the needful work, if anyone feels like using Rust instead.
Nobody's stopping you from using the NDK to compile Rust though. Android's ABI is just an ABI like any other. The system doesn't care if you built an .so using Rust or anything else so long as it plays by the rules.
Some people rather reach out for first party support, instead of filling in the gaps for the big boys.
By first party support, would you be expecting a Rust toolchain shipped in the NDK, or maybe Rust bindings shipped in the NDK?
I could see the latter, although I'd still question whether they should be special cased in terms of a Rust dependency compared to bindings being hosted on crates.io.
Or maybe they should ship scripts that shell out to an existing Rust toolchain.
I expect Rust being documented here,
https://developer.android.com/ndk
I expect the whole Rust build process being part of Android Studio, including mixed language debugging between Java, Kotlin and Rust.
I expect all NDK APIs to have Rust bidding crates.
I expect that Android developer forums also care to support devs using Rust.
And anything else that I forgot to mentioned, that is provided for Java, Kotlin, C and C++.
The Android NDK just barely supports C and C++ either, unless you're ok with 1990's tooling standards. The whole thing feels like it's maintained by two dudes locked in a Google basement somewhere. I doubt they have the capacity to deal with Rust support in the NDK, unless there's a big strategic change in Android tooling.
I'm not sure what you mean by this. Looking at a sample app [1] for the NDK, the tooling appears to be gradle and Cmake. Cmake isn't the newest of tools, but it's also not that dated.
[1] https://github.com/android/ndk-samples/tree/main/endless-tun...
Nope it clearly supports ISO C 11 and ISO C++17.
I do agree the NDK team is rather small.
Huh? "Barely"? What are you talking about? The NDK supports compiling C++ just fine. There are CMake files, among other things, ready to use --- as well as Soong native configurations. CMake is under active development. Other toolkits are within easy reach: for example, Meson works fine. Also Bazel. Hell, even autotools can be made to work.
Don't forget this list of expectations for SPARK [1] too, if you want even safer, high-integrity, and formally verified code on Android!
I think we can indeed forget about it.
Android has zero lines of Ada code, why should I care about it in this case?
They've been trying to strangle the NDK for years. You can't even make an app without a ton of glue code on the JVM.
True, NDK was never intended to make apps, only for games and as helper for native methods, since it was introduced in Android 2.1.
The point is about the official support for using Rust exactly for the same use cases.