Just because emotions seem to be always boiling whenever Rust is mentioned, I want to clarify for people who only follow casually that there is nothing unusual or scandalous about these finds.
The Rust project as a whole currently has 178 open issues with the tag I-unsound[1]. Many of these describe issues that only exist in the nightly compiler, of course, but it does happen that bugs are not caught before shipping. That's software.
That there's nothing unusual about these finds, the Drop static lifetime bug, or cve-rs is proof that Rust is not, and probably never will be, memory-safe. It's still an improvement though.
Is a language only memory-safe if its implementations are completely bug-free? I guess no language can ever be memory-safe then, might as well throw out the concept.
> there is nothing unusual or scandalous about these finds
I would not go so far. I just mentioned in a separate comment, a minute before writing this here, that it is rather
outrageous that the premier language for AI these days (aka Rust),
has not used AI to find these bugs before. Would you not
also consider that it should follow from simple logic that
a language that is the premier AI language now, also would
use AI as it preaches for it, to find bugs?
I’m having a hard time understanding you here. Are you saying that since rust is a good language for AI to write code in, it says something about the (lack of) power of AI, that it wasn’t what found the bug? Because the logic there seems twisted: “F1 drivers, the biggest users of F1 cars, almost never make improvements to F1 engines”.
Why would you think that? Bugs exist in complex software no matter how hard you try, including software written in Rust such as the Rust compiler and standard library. Did you expect the highly competent people working on these projects to just one-shot everything?
Actually, software that probably has no bugs does exist, starting with the seL4 microkernel. Such software is usually pretty simplistic because that goes a long way towards proving an absence of bugs.
More practically, however, Rust has proven to have more memory safety bugs than GC languages. If you want memory safety you should go GC. If you want consistent performance or tight control over memory, and pretty good memory safety, then you should go Rust.
From the article: "The findings reveal three recurring patterns of misplaced trust in unsafe code: trusting code not to panic, trusting user-provided implementations to be correct, and trusting the underlying system."
The call to "panic" is an effect and can be checked at compile time. Haskell does this with MonadError class, marking up code that may throw.
Rust has Turing complete type system for years and still does not have effects of any kind.
To be fair, "trusting user provided implementation" is also has something to do with effects. Proper type system would require user-provided implementation to use only certain effects and nothing else.
So two of of found shortcomings are directly related to the type system and, specifically, to the choice of original Rust implementors to not to distinguish between pure and effectful computations.
Do Rust implementors highly competent? Yes, of course! They work on Turing complete type system. Will they eliminate two of three recurring patterns? I strongly doubts so. It is a design decision and these are especially hard to change. Changing it will also break all Rust source code produced to that point of time.
Is it a scandalous situation? For me, it is. The situation is so dire, actually, that "highly competent people working" has to be defended with trivially true fact that "bugs exist in complex software no matter how hard you try," instead of addressing core problem.
You're pointing out that there can exist a language other that Rust that is even safer, and prevents even more classes of bugs. That's trivially true, of course, but this language doesn't currently exist.
You may have noticed the wave of "rewritten in rust" software in recent years, whose only (?) selling point was that this makes the software somehow automatically safe and if not bug free then at least significantly lower on bugs.
This finding is just yet another nail in the coffin of that myth.
I have never understood this argument. Rust, by default, is safer. This is trivially true. But it also is not perfect.
This entire argument against Rust hinges on it somehow invalidating all of the improvements it does have, all the safety it does guarantee, just because it also has a few bugs. It's neither a myth that Rust is safer, nor is it a nail in the coffin for it.
Otherwise you must make the argument that just because unsafe exists, and is used, it is automatically as bad as let's say C - a language that has proven time and time again how trivial it is to make mistakes with. Something that just doesn't happen in Rust.
In addition to that, many of reported soundness issues rely on adversarial implementations for stdlib' traits by user's types, like Borrow::borrow or as_ref returning different value between back to back calls. You would do that if you try to break compiler, but not when writing regular programs.
The annoyance comes from the fact that your kind of _reasonable_ discourse on safety is always thrown when there is a bug, but never when there is a claim that rust is 100% by construction. I.e. It's always a defensive argument, but I never see someone step up to mention it on overly optimistic claims.
I can't be defending the statements of whoever says this, but I do think that the reasonable way to understand it is that the fundamental model that the Rust compiler and standard library attempt to implement is safe and correct by construction, rather than as a statement about the concrete implementation.
Among software developers, we shouldn't need to provide a "assuming there are no bugs" caveat with every statement about how something works, because we know that bugs are essentially unavoidable at scale. We can say that Rust makes it much easier to avoid bugs, but nobody serious has ever claimed that they are 100% eliminated.
It's not like C or C++ compilers are bug free either, despite implementing much weaker models.
> the fundamental model that the Rust compiler and standard library attempt to implement is safe and correct by construction
This comes up a lot as a defensive argument too, but it's also pretty frustrating, because Rust's "fundamental model" is not a thing that really exists. There are project proposals to create one (such as https://goals.rust-lang.org/2026/roadmap-project-zero.html#), but at the moment it's just a vague set of vibes people gesture towards to dismiss all the problems with Rust-as-actually-implemented.
If you would like the Rust language to have a formal specification, I would suggest you look at ways to help that happen. There is already the Ferrocene[1] project.
The safety model of Rust is extremely well described in detail. It's not particularly hard to understand, either. Are you worried that there is some kind of fundamental flaw in the design, we're just waiting to discover it, and it will all come crumbling down? My suggestion would be to start by understanding Rust's type system and why it works the way it does, and you will very quickly build an intuition ("vibes") that it obviously works.
If your intuitive understanding of Rust is that its type system "obviously works", then you have developed an incorrect understanding! There are many documented cases where it does not work, and there is an active project to change its design so it works in more cases.
I don't think we should accept this kind of vibes-based reasoning from C programmers who are confident their programs don't have any memory bugs, and we shouldn't accept it from people trying to sell us programming languages either.
I think it's reasonable to say that Rust successfully prevents a lot of memory issues, and that it's it's a good choice for lots of projects. But it frustrates me when people try to dishonestly argue that it's perfect and then retreat to arguing that the implementation is flawed but the design is perfect, because neither statement is true but they take a lot of oxygen away from efforts to improve the situation.
I mean it would be nice to make one of those, but I also don't really see the point of doing so besides shutting up the guys who like to talk about how Rust doesn't have a spec, most of whom do not even seem involved in the language really? Nobody really has an issue with the current situation, they are all clearly bugs and they more or less get triaged appropriately.
Funny. I would think a GCed language was safer - maybe Haskell being the safest of all (outside even more niche languages) for its ability to encode so much in the type system.
Yet, for some curious reason, a substantial group of snake oil salesmen insist that only this one particularly cut puzzle piece (rust) fits the bill.
It has been known for decades that GC was one way to achieve memory safety. That was one of the main selling point of Java back in the 90s - no more segfaults.
Rust was an absolute breakthrough, because it provides a second way to gain memory safety, this time without the significant drawbacks of a garbage collector, bringing memory safety to systems programming. That's materially different.
The only other previous attempt at this was Ada, which for a variety of reasons never quite fit the bill despite its obvious strengths.
I'm on your side of this, but there are Rust zealots out there and they're incredibly annoying.
(Edit to add:)
As a longtime Java guy (after a decade or so as a C++ guy) what drew me to Rust wasn't actually memory safety; that was table-stakes for me and Go might have lured me in if that was all I wanted. The low-cost (cognitive complexity is a cost) of memory safety was a factor, but it was the expressive type system that sealed the deal.
Also you gotta write the garbage collector itself in some language that isn't itself garbage collected - usually C or C++, before Rust existed.
Anyway, it's basically slander to say that people advocating Rust are snake oil salesman. Rust is a genuine improvement to systems languages over the existing options (primarily C/C++) in multiple respects, not just memory safety; it is not a panacea that solves every problem or eliminates every possible bug, and no one involved with the project ever claimed that it did.
I, like many others, still think that a language that requires a bug in the compiler for a certain category of error to be exhibited is preferable to one that has no such guarantees. I like C but it's not crazy to consider rewriting a tool in Rust for memory safety reasons.
In most discussions involving Rust I hear those “counter” criticisms, but is it a common claim by Rust proponents that Rust makes software bug free, etc.? I feel like I don’t regularly encounter the original claim that others regularly argue against.
I've always felt that parts of the Rust standard library to be weirdly un-rusty. Like indexing a vector panicking instead of returning a Some(T) in case the index is out of bounds. It's easy to avoid if you put the effort in of course, but it did always feel weird that one of the first data structures you learn has this behavior right out of the gate.
That's not really a memory safety bug, due to the fact that it panics rather than doing something unsafe.
The `variable[i]` notation is intended to function like in other languages, where it returns an unwrapped value. It would be ugly, noisy, unergonomic, and annoying to have to properly handle `Some` (like in `variable[i].unwrap()`) every single time you ever used it. Imagine doing `c[i].unwrap()[j].unwrap() += a[i].unwrap()[k].unwrap() * b[k].unwrap()[j].unwrap();`.
If you really want the result to be wrapped in `Some`, you can use `variable.get(i)`.
> That's not really a memory safety bug, due to the fact that it panics rather than doing something unsafe.
The ? operator can be nicer to read then all those .unwraps() as an alternative and doesn't panic.
I guess the concern with panics is you're not forced to think about how you want to handle them like you do when you get a `Result` or `Option` returned.
As a result it could be used as a DDOS against your app if it's network accessible.
Rust does VERY limited breaking changes, the largest one probably being the `!` type inference change that was very carefully vetted, deemed a necessity and saw contributions to fix the issue in all crates in the official registry
They do break backwards compatibility at edition boundaries, but if they do that too much without a good enough reason they could end up with a python2->python3 situation, with low uptake.
Applying the ? operator to an Option<_> doesn't work inside a function returning io::Result<_>, for example, and turning the None from an out-of-bounds vector access into some io::Result::Err usually wouldn't make sense.
I would go so far as to say that an out-of-bounds index is always a logic error (to be handled by panicking), not an input error (to be handled with Result). And if the out-of-bounds access was caused by invalid input, then logic error consists of not checking the input before indexing.
The former issue arises from post-hoc thinking. Of course it's hard to retrofit into the language, but I see no reason why there shouldn't be facilities for the ? operator to turn a None into an appropriate error type.
The latter is a subjective matter. There are reasons to never want a panic (hello there, Linux kernel) and reasons to do more sophisticated stuff (core dump, cleanup or notifications) before crashing. And maybe one thread shouldn't lead to process death. I think most of these decisions should be kept to the user's discretion.
I think it is a reasonable tradeoff, especially since `get(i)` is available to do what you want. An array index oob error is usually a logic issue, not some recoverable error, and in a lot of cases you're doing it where it is impossible to be wrong, so it is a lot of error-checking (and giving up things like x[0] += 1) to have the Index trait return an Option instead of a direct reference.
Although its method signature is kind of cursed due to generics, it matches your requirement that allows graceful OOB handling.
If the default indexing behavior were like `.get()` it would be too cumbersome to express repeated use of indexing, or multi-dimensional array indexing, etc.
Also there is get_unchecked which is unsafe. Trivially you'll trigger OOB access when misused.
There is also the idea that the language is so good because such things are caught at compile time via the type system. (But this is generally not done for the same reason it is not done in C.)
As shown in PL/I or NEWP, C could have done it both ways, checked and unchecked when really required, e.g. PREFIX(SUBSCRIPTRANGE), PREFIX(NOSUBSCRIPTRANGE).
Likewise regarding the storage for the length, if one is willing to go the way C went down a few years later, declare a pointer and make the array storage be pointer based, e.g. BASED(MY_ARRAY_PTR).
The language can catch a lot of things via the type system, but there are always going to be some things that are just not in scope and must be asserted via precondition. This is one of them.
Wrapping everything in optional does lead to optional fatigue, and people just unwrap each optional they get since they know the index is inbounds, missing the one instance where it really is possible to get None.
Maybe optional fatigue is Rusty but it's not a good style in any language
> But many operations in Rust can panic, from out-of-bounds indexing to calls into user-provided code, and by default a panic unwinds
That's why I think having unwinding in Rust is a mistake. It gives a false sense of safety, but it's very hard to write unwinding-correct code. So, it would be better to handle panics as immediate process termination instead.
But that would make Rust unsuitable for writing anything that wants to keep the program running even if something does panic - say, test harness, or a plugin system.
This is already the case for any other non-JVM language though, and they usually manage to deal with it by separating operating system processes. A lot of C programs have plugins (even the kernel!) and don't crash because the plugins aren't buggy (by which I don't mean they have no bugs).
Your plugin can still crash in ways other than a panic, like a segfault inside an unsafe block.
It's also likely possible to DoS the application from a plugin in ways other than a crash, e.g. add a billion entries to the right-click menu.
And if you're loading a plugin, how do you know the plugin was actually written in Rust, anyway? It could be written in C and scribble over all your stack memory as soon as you load it.
It's very hard to guarantee stability in the presence of arbitrary extensions, and you might not even want to because someone might have a real use case for an extremely large right-click menu (probably not a billion though). I don't think even language server protocol guarantees it.
OTOH that panics are ok and easy to reach for and unwind is why you have to handle them and keep the program running.
I don’t think that an unwind-less rust would have fit within the weirdness budget 10 years ago, today it would definitely be something to consider. Although it would have an unwind effect to contend with.
It would be entirely unsuitable for the kind of project I work on in my day job, which requires running user-provided code (trusted code, but one that may contain many bugs) in 100 different worker threads, and if one of them crashes the other 99 must keep running. Without unwinding and recovery, we'd have to completely redesign the whole application, probably to multiprocess architecture with all its downsides.
What do you do if they segfault on raw pointers? Does Rust's runtime catch SIGSEGV and translate it to a panic?
You can also get SIGBUS by mapping a file beyond the end of the file and then trying to read the extra bytes. If Rust's version of mmap checks this, you can still do it by truncating the file while it's mapped.
But that's the point see? You have to catch panics because it's easy, straightforward and "common" to trigger panics.
If it were not, you wouldn't, because the buggy user-provided code would not be triggering panics, in the same way you're not catching `std::process::abort` or segfaults or whatnot.
An effect system could gelp us reason about code by making calls to panic-capable functions marked as such. Maybe we could mark some types as panic unsafe?
Very interesting finds for sure. But taking the "trusting user-provided code" as an example: isn't this just a case of malicious code being malicious? I understand the point that the malicious act comes through memory unsafety, and it's a big gotcha that safe Rust (i.e. Rust without `unsafe`) can trigger memory unsafe behavior. But the code doing it is still acting maliciously (by having an `AsRef` implementation that returns different slices at different times). And the code being abused (the one that does the unsafe indexing) does have `unsafe` – so this is a case of the user writing unsafe Rust that interacts with malicious safe Rust.
I don't know if it's ever been in Rust's threat model to handle malicious code.
Sufficiently deniable malice is indistinguishable from incompetence. If a condition can be triggered "maliciously", then it can probably be also triggered inadvertently. There's no point in saying "that's malicious so it doesn't count".
When I write such an explanation it will probably use Comte as an example. Comte implements ExactSizeIterator which is a safe trait for claiming your iterator gives accurate information about how many items are left. It's lying (but equally a safe implementer might just be mistaken), and Comte is not in fact giving accurate size information. However, if you need to rely on a promise that the iterator length is correct Rust has an unsafe trait for such promises named TrustedLen.
Unlike Comte, mis-implementing TrustedLen could cause Memory Unsafety, but you would need to write the `unsafe` keyword and be lying or mistaken, which is what Rust promised you will happen.
To give a concrete example, if I tell Rust I want to put the results from this iterator over Goose into an array [Goose; 6] in usual cases it will emit code to check for overflow and panic if the Iterator makes the wrong number of Goose, even if the iterator implements ExactSizeIterator and claims to have six Goose, that panic check happens. But if it implements TrustedLen and makes the same claim, the compiler will believe it and skip the check. If it was wrong about having six Goose, your program is nonsense and anything might happen.
Only JavaScript partially supports this, based on the domain where the code originated from, so your website code is not infected by the Javascript run by an ad you embed.
I sure wish they'd rewrite the Azure Linux VM Agent in Rust. That thing normally burns 10-15% of a core, and also blocks VM restart for tens of minutes, god knows why.
I've seen certain MSFT engineers get a lock ordering warning from taking mutexes in a wrong order and "fix" it by using a spin loop + try lock instead.
If it only retries because a different thread made progress, it's lock-free and total work is bounded - even though one particular thread could get very unlucky, system-wide luck is conserved.
How much code's inside the CAS loop? If it's being held for less than (sticks finger in the wind) a few thousand of instructions then backoff or limits are actively counterproductive. That's how you're supposed to use a CAS loop.
If it's more, you shouldn't try to invent a more clever CAS loop, you should replace it with a mutex.
> If it's more, you shouldn't try to invent a more clever CAS loop, you should replace it with a mutex.
Also, if your language isn't old and crap your mutex already knows about this trick and so you should default to using a mutex, because it will do that anyway, and only write your own CAS loop if you have measured that it matters.
Note that word measured. Since we're talking about Rust here and people doing this stuff should be used to writing SAFETY justifications in their Rust, your CAS loop should have an accompanying justification comment.
Does the system provide ready made backoff and / or bounded CAS loop primitives? Because if you have to write the entire thing in full every time I can see why it’s get skipped.
The best AI language has not used AI to find all bugs yet?
I don't quite like AI, but I think from an objective point of
view one can reason that AI led to the discovery of more bugs,
if we deduct the non-bugs from those reports.
The community-driven quest for "panic safety" in Rust is the same curse that befell C++ in terms of "exception safety", maybe worse because at least more C++ shops are sensible enough to disable exceptions. It's an exercise put forth by language lawyers that increases program complexity beyond comprehension, often without achieving the stated goal. It's simply not practical to think through 20 convoluted edge cases for every innocent little function, which means that no realistic program will safely recover from a panic. It's best to admit this and engineer your system accordingly.
To me, panic being an unwind feels like Rust's biggest sin. It's especially problematic IMO since an idiomatic Rust program would use Result and pass errors as function returns, except the panic unwind machinery sits there as a hidden return path that's very easy to forget about. It's the same problem as writing exception safe C++, every function call can turn into an early exit from the function.
Most Rust learning material teaches panic as if it's an abort. They don't lie about it unwinding, but 99% of uses of panic use it like a fatal error with no expectation of recovery. The fact you can _recover_ from panics is even worse because you can then continue running a program after one of these "fatal" errors failed to abort the process.
There's some powerful things panic/catch_unwind let you do. I'm yet to be convinced the complexity imposed on all code is worth the squeeze on the cases where panic = unwind is a genuine requirement.
I wish the default was panic = abort, but as a whole I still find Rust the best tool for where I use it.
This is such a deep topic with literally decades of discourse behind it.
I tend to agree with the general principle that panicking or throwing an exception should terminate the process in most cases. But those last microseconds of a process' lifetime matter quite a lot: Are you able to save the user's work? Are you able to log the error so the crash can be diagnosed?
With Undefined Behavior in the picture, you cannot do anything. The whole process is toast, and its entire memory state is invalid.
I would say the problem with this becomes that it’s all best effort.
You can’t rely on it for correctness because for your software to be reliable it needs to deal with an abrupt power outage and recover.
Best effort diagnostics make sense. And the “Rust 2.0” or successor language will with certainty close this gap.
But I also understand why we are here. A decade ago when 1.0 came out Rust had already used up most of its complexity budget for traits, enums, borrow checker, generics, functional style programming and so on.
Diverging even further from what was expected by the majority of programmers would very likely have left Rust as an esoteric language with a tiny following. Instead of what it became.
And back then unsafe was thought to work more like “just do like in C”.
There has been years of research formalizing how it actually works that’s brought us to the current understanding where it’s not trivial to write it correctly.
> The community-driven quest for "panic safety" in Rust is the same curse that befell C++ in terms of "exception safety", maybe worse because at least more C++ shops are sensible enough to disable exceptions.
You seem confused. The "exception safety" topic in C++, which clueless people often cite Google's style guide as an appeal to authority, is that it's undesirable to modify old legacy code that does not use exceptions to call libraries that do. The sole reason this is not desirable is that calling code that throws exceptions introduces code paths that the legacy code was not designed to consider (i.e., all stack unwinding and RAII cleanup in surprising points with surprising implications).
And project managers tasked with a project that involves legacy C++ code can't simply spare time and resources to redesigns legacy project to accommodate for these changes, or the risk of having stable production code one day crash just because someone linked to a library that now throws.
Therefore, the "exception safety" discussion in C++ is not a technical one. It is a project management one, and one which involves high reliable legacy code.
Rust is unsafe by spec, not just by implementation. For memory and type safety. concurrency safety is impossible to get with their blocking IO approach.
I think that the point of Rust as far as memory safety isn't perfection. The point is centralizing proper memory safety.
With C, every single programmer has to properly account for memory safety in just about every program they write. If you fix a memory safety bug in one program, that's not necessarily going to help any other program.
With safe Rust, that work has largely been done for you. If a memory safety bug is found and fixed in Rust's standard library, it's found and fixed for every program that uses safe Rust (with a recompile using the new standard library version).
The point is for some experts to work together to get things as memory safe as possible in one place instead of everyone having to get things as memory safe as possible in every place.
There were also memory safety bugs found in Rust code without frontier models before. Note that I do not deny that Rust has an advantage with regard to memory safety (at the cost of other things, including supply chain issues), but it is massively overhyped.
On the other hand, if your safe Rust program segfaults, you should consider submitting an issue to the rust project. If your C program crashes, you should probably use a debugger.
As someone on HN phrased it "a segfault is the protection layer stepping it", so a C program having a segfault is not worse than Rust panicking.
And to the GP: yes, if your safe Rust program segfaults because of a bug in the Rust standard library, then you file a bug with the Rust project. I agree this is nice!
But in most real-world cases, your Rust program will segfault (or panic) due to a bug in your own code or in some code in one of its many dependencies. Then you also debug it.
In depends. In many cases where you have a panic in Rust, the segfault in C would also leave the memory content uncorrupted.
The problem is if the program misbehaves in some unexplainable way due to random corruption. And memory safety is the right path to reduce such problems. But only in the imaginary world of perfectly safe Rust code is this a reality, or with Fil-C, or verified C code, or some other memory safe language. In the actual world of most systems programming scenarious even with Rust it is only somewhat better than C.
Segfault. Then you get to use gdb/valgrind like anyone else (although you also have Miri).
The strategy when integrating unsafe segments to your Rust codebase is that you provide a safe interface for them, then very carefully review the code, and also use tools such as Miri to test them. Ideally your business side would not have any unsafe code in it.
True but C never went out of its way to highlight memory safety features and shut down (with a rather aggressive posture to boot) any criticism of the language with arguments revolving around this feature as if it was the Christ's second coming.
In lieu of finding out that there are in fact multiple memory issues in std is at least amusing and some might say well deserved egg in the face of the more, shall we say, enthusiastic defenders of the language.
Indeed though only those who taut the "memory safety" line as a mantra and some kind of catchall counterargument to any and all discussion that even remotely speaks ill of their favorite tool. Fanatics as you so aptly put.
I have my share of criticism of Rust. But even a safe language has to deal with unsafe memory etc. at some point in the stack. The goal is to minimize such uses of unsafe and for the cases of unsafe that exist to prove that they are safe.
These haha gotcha's are criticism leveled at language designers who never denied that there is unsafe code, that there can be bugs in unsafe code, and that such bugs can leak to safe code.
egg in the face
Is it an egg in the face when it requires LLM analysis to find these nowadays? It tells me that the Rust developers got really good at eliminating memory bugs that are lurking in unsafe code.
Also, this doesn't change anything to the benefits of Rust's ownership model, which removes classes of bugs that are very common in C/C++, so the Rust advocates were still right.
(I'll leave my rants about Rust in a thread where it's more appropriate, but they are not about memory safety.)
The Rust project as a whole currently has 178 open issues with the tag I-unsound[1]. Many of these describe issues that only exist in the nightly compiler, of course, but it does happen that bugs are not caught before shipping. That's software.
[1]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+state%...
What do you call a programming language which claims to be memory-safe that has memory safety issues in it's standard library?
I would not go so far. I just mentioned in a separate comment, a minute before writing this here, that it is rather outrageous that the premier language for AI these days (aka Rust), has not used AI to find these bugs before. Would you not also consider that it should follow from simple logic that a language that is the premier AI language now, also would use AI as it preaches for it, to find bugs?
that there is nothing unusual is the scandal
More practically, however, Rust has proven to have more memory safety bugs than GC languages. If you want memory safety you should go GC. If you want consistent performance or tight control over memory, and pretty good memory safety, then you should go Rust.
Citation needed I think. Where was this proven? Who measured and on which "GC languages" ?
The call to "panic" is an effect and can be checked at compile time. Haskell does this with MonadError class, marking up code that may throw.
Rust has Turing complete type system for years and still does not have effects of any kind.
To be fair, "trusting user provided implementation" is also has something to do with effects. Proper type system would require user-provided implementation to use only certain effects and nothing else.
So two of of found shortcomings are directly related to the type system and, specifically, to the choice of original Rust implementors to not to distinguish between pure and effectful computations.
Do Rust implementors highly competent? Yes, of course! They work on Turing complete type system. Will they eliminate two of three recurring patterns? I strongly doubts so. It is a design decision and these are especially hard to change. Changing it will also break all Rust source code produced to that point of time.
Is it a scandalous situation? For me, it is. The situation is so dire, actually, that "highly competent people working" has to be defended with trivially true fact that "bugs exist in complex software no matter how hard you try," instead of addressing core problem.
This finding is just yet another nail in the coffin of that myth.
This entire argument against Rust hinges on it somehow invalidating all of the improvements it does have, all the safety it does guarantee, just because it also has a few bugs. It's neither a myth that Rust is safer, nor is it a nail in the coffin for it.
Otherwise you must make the argument that just because unsafe exists, and is used, it is automatically as bad as let's say C - a language that has proven time and time again how trivial it is to make mistakes with. Something that just doesn't happen in Rust.
Among software developers, we shouldn't need to provide a "assuming there are no bugs" caveat with every statement about how something works, because we know that bugs are essentially unavoidable at scale. We can say that Rust makes it much easier to avoid bugs, but nobody serious has ever claimed that they are 100% eliminated.
It's not like C or C++ compilers are bug free either, despite implementing much weaker models.
This comes up a lot as a defensive argument too, but it's also pretty frustrating, because Rust's "fundamental model" is not a thing that really exists. There are project proposals to create one (such as https://goals.rust-lang.org/2026/roadmap-project-zero.html#), but at the moment it's just a vague set of vibes people gesture towards to dismiss all the problems with Rust-as-actually-implemented.
If you would like the Rust language to have a formal specification, I would suggest you look at ways to help that happen. There is already the Ferrocene[1] project.
The safety model of Rust is extremely well described in detail. It's not particularly hard to understand, either. Are you worried that there is some kind of fundamental flaw in the design, we're just waiting to discover it, and it will all come crumbling down? My suggestion would be to start by understanding Rust's type system and why it works the way it does, and you will very quickly build an intuition ("vibes") that it obviously works.
[1]: https://ferrocene.dev/
I don't think we should accept this kind of vibes-based reasoning from C programmers who are confident their programs don't have any memory bugs, and we shouldn't accept it from people trying to sell us programming languages either.
I think it's reasonable to say that Rust successfully prevents a lot of memory issues, and that it's it's a good choice for lots of projects. But it frustrates me when people try to dishonestly argue that it's perfect and then retreat to arguing that the implementation is flawed but the design is perfect, because neither statement is true but they take a lot of oxygen away from efforts to improve the situation.
Yet, for some curious reason, a substantial group of snake oil salesmen insist that only this one particularly cut puzzle piece (rust) fits the bill.
It has been known for decades that GC was one way to achieve memory safety. That was one of the main selling point of Java back in the 90s - no more segfaults.
Rust was an absolute breakthrough, because it provides a second way to gain memory safety, this time without the significant drawbacks of a garbage collector, bringing memory safety to systems programming. That's materially different.
The only other previous attempt at this was Ada, which for a variety of reasons never quite fit the bill despite its obvious strengths.
I'm on your side of this, but there are Rust zealots out there and they're incredibly annoying.
(Edit to add:)
As a longtime Java guy (after a decade or so as a C++ guy) what drew me to Rust wasn't actually memory safety; that was table-stakes for me and Go might have lured me in if that was all I wanted. The low-cost (cognitive complexity is a cost) of memory safety was a factor, but it was the expressive type system that sealed the deal.
I mean, I keep hearing about them, and they just literally never show up anywhere I am, and I'm pretty active in the Rust community.
Anyway, it's basically slander to say that people advocating Rust are snake oil salesman. Rust is a genuine improvement to systems languages over the existing options (primarily C/C++) in multiple respects, not just memory safety; it is not a panacea that solves every problem or eliminates every possible bug, and no one involved with the project ever claimed that it did.
I, like many others, still think that a language that requires a bug in the compiler for a certain category of error to be exhibited is preferable to one that has no such guarantees. I like C but it's not crazy to consider rewriting a tool in Rust for memory safety reasons.
> bug free
In most discussions involving Rust I hear those “counter” criticisms, but is it a common claim by Rust proponents that Rust makes software bug free, etc.? I feel like I don’t regularly encounter the original claim that others regularly argue against.
The `variable[i]` notation is intended to function like in other languages, where it returns an unwrapped value. It would be ugly, noisy, unergonomic, and annoying to have to properly handle `Some` (like in `variable[i].unwrap()`) every single time you ever used it. Imagine doing `c[i].unwrap()[j].unwrap() += a[i].unwrap()[k].unwrap() * b[k].unwrap()[j].unwrap();`.
If you really want the result to be wrapped in `Some`, you can use `variable.get(i)`.
The ? operator can be nicer to read then all those .unwraps() as an alternative and doesn't panic.
I guess the concern with panics is you're not forced to think about how you want to handle them like you do when you get a `Result` or `Option` returned.
As a result it could be used as a DDOS against your app if it's network accessible.
I would go so far as to say that an out-of-bounds index is always a logic error (to be handled by panicking), not an input error (to be handled with Result). And if the out-of-bounds access was caused by invalid input, then logic error consists of not checking the input before indexing.
The latter is a subjective matter. There are reasons to never want a panic (hello there, Linux kernel) and reasons to do more sophisticated stuff (core dump, cleanup or notifications) before crashing. And maybe one thread shouldn't lead to process death. I think most of these decisions should be kept to the user's discretion.
There are facilities for this.
For "fallible" variant indexing, you have `the_slice.get(index)`: https://doc.rust-lang.org/stable/std/primitive.slice.html#me...
Although its method signature is kind of cursed due to generics, it matches your requirement that allows graceful OOB handling.
If the default indexing behavior were like `.get()` it would be too cumbersome to express repeated use of indexing, or multi-dimensional array indexing, etc.
Also there is get_unchecked which is unsafe. Trivially you'll trigger OOB access when misused.
If you are doing this then you've made a mistake in your code. Rust is consistent in catching these early.
Likewise regarding the storage for the length, if one is willing to go the way C went down a few years later, declare a pointer and make the array storage be pointer based, e.g. BASED(MY_ARRAY_PTR).
Alternatively when allocating the array storage define its access to be pointer based.
This means no length descriptor will be stored alongside the array, and the access to array elements will be done via the given pointer.
It is up to the programmer not to mess up then.
Unfortunately C authors never added a way to do the former, given their experience with PL/I and Multics.
I am aware we are finally going into that direction.
Maybe optional fatigue is Rusty but it's not a good style in any language
That's why I think having unwinding in Rust is a mistake. It gives a false sense of safety, but it's very hard to write unwinding-correct code. So, it would be better to handle panics as immediate process termination instead.
Your plugin can still crash in ways other than a panic, like a segfault inside an unsafe block.
It's also likely possible to DoS the application from a plugin in ways other than a crash, e.g. add a billion entries to the right-click menu.
And if you're loading a plugin, how do you know the plugin was actually written in Rust, anyway? It could be written in C and scribble over all your stack memory as soon as you load it.
It's very hard to guarantee stability in the presence of arbitrary extensions, and you might not even want to because someone might have a real use case for an extremely large right-click menu (probably not a billion though). I don't think even language server protocol guarantees it.
I don’t think that an unwind-less rust would have fit within the weirdness budget 10 years ago, today it would definitely be something to consider. Although it would have an unwind effect to contend with.
You can also get SIGBUS by mapping a file beyond the end of the file and then trying to read the extra bytes. If Rust's version of mmap checks this, you can still do it by truncating the file while it's mapped.
But that's the point see? You have to catch panics because it's easy, straightforward and "common" to trigger panics.
If it were not, you wouldn't, because the buggy user-provided code would not be triggering panics, in the same way you're not catching `std::process::abort` or segfaults or whatnot.
reminds me of the tales from https://oxide.computer/blog/iddqd-unsafe
Effect systems usually fail because effects are very different from each other and can't be generalised.
It is just the sketchiest feature in a very realiable language.
It doesn't make sense to me why anyone whould use rust with panic handling.
And the of course the language trusts the operating system, how else can you program?
This kind of thing is basically why all projects blocked AI issues, it is just slop that kind of makes sense if you don't know what you are doing.
I don't know if it's ever been in Rust's threat model to handle malicious code.
Note that incorrect code can still cause
1. incorrect outputs, or
2. arbitrary performance degredations
safe rust doesn't have to work magic. All it must do is prevent UB.
I thought maybe I explain this well in the README for https://github.com/tialaramex/misfortunate/ but apparently not, so that's on my FIXME list now.
When I write such an explanation it will probably use Comte as an example. Comte implements ExactSizeIterator which is a safe trait for claiming your iterator gives accurate information about how many items are left. It's lying (but equally a safe implementer might just be mistaken), and Comte is not in fact giving accurate size information. However, if you need to rely on a promise that the iterator length is correct Rust has an unsafe trait for such promises named TrustedLen.
Unlike Comte, mis-implementing TrustedLen could cause Memory Unsafety, but you would need to write the `unsafe` keyword and be lying or mistaken, which is what Rust promised you will happen.
To give a concrete example, if I tell Rust I want to put the results from this iterator over Goose into an array [Goose; 6] in usual cases it will emit code to check for overflow and panic if the Iterator makes the wrong number of Goose, even if the iterator implements ExactSizeIterator and claims to have six Goose, that panic check happens. But if it implements TrustedLen and makes the same claim, the compiler will believe it and skip the check. If it was wrong about having six Goose, your program is nonsense and anything might happen.
How much code's inside the CAS loop? If it's being held for less than (sticks finger in the wind) a few thousand of instructions then backoff or limits are actively counterproductive. That's how you're supposed to use a CAS loop.
If it's more, you shouldn't try to invent a more clever CAS loop, you should replace it with a mutex.
Also, if your language isn't old and crap your mutex already knows about this trick and so you should default to using a mutex, because it will do that anyway, and only write your own CAS loop if you have measured that it matters.
Note that word measured. Since we're talking about Rust here and people doing this stuff should be used to writing SAFETY justifications in their Rust, your CAS loop should have an accompanying justification comment.
I don't quite like AI, but I think from an objective point of view one can reason that AI led to the discovery of more bugs, if we deduct the non-bugs from those reports.
Most Rust learning material teaches panic as if it's an abort. They don't lie about it unwinding, but 99% of uses of panic use it like a fatal error with no expectation of recovery. The fact you can _recover_ from panics is even worse because you can then continue running a program after one of these "fatal" errors failed to abort the process.
There's some powerful things panic/catch_unwind let you do. I'm yet to be convinced the complexity imposed on all code is worth the squeeze on the cases where panic = unwind is a genuine requirement.
I wish the default was panic = abort, but as a whole I still find Rust the best tool for where I use it.
I tend to agree with the general principle that panicking or throwing an exception should terminate the process in most cases. But those last microseconds of a process' lifetime matter quite a lot: Are you able to save the user's work? Are you able to log the error so the crash can be diagnosed?
With Undefined Behavior in the picture, you cannot do anything. The whole process is toast, and its entire memory state is invalid.
You can’t rely on it for correctness because for your software to be reliable it needs to deal with an abrupt power outage and recover.
Best effort diagnostics make sense. And the “Rust 2.0” or successor language will with certainty close this gap.
But I also understand why we are here. A decade ago when 1.0 came out Rust had already used up most of its complexity budget for traits, enums, borrow checker, generics, functional style programming and so on.
Diverging even further from what was expected by the majority of programmers would very likely have left Rust as an esoteric language with a tiny following. Instead of what it became.
And back then unsafe was thought to work more like “just do like in C”.
There has been years of research formalizing how it actually works that’s brought us to the current understanding where it’s not trivial to write it correctly.
You seem confused. The "exception safety" topic in C++, which clueless people often cite Google's style guide as an appeal to authority, is that it's undesirable to modify old legacy code that does not use exceptions to call libraries that do. The sole reason this is not desirable is that calling code that throws exceptions introduces code paths that the legacy code was not designed to consider (i.e., all stack unwinding and RAII cleanup in surprising points with surprising implications).
And project managers tasked with a project that involves legacy C++ code can't simply spare time and resources to redesigns legacy project to accommodate for these changes, or the risk of having stable production code one day crash just because someone linked to a library that now throws.
Therefore, the "exception safety" discussion in C++ is not a technical one. It is a project management one, and one which involves high reliable legacy code.
Rust is unsafe by spec, not just by implementation. For memory and type safety. concurrency safety is impossible to get with their blocking IO approach.
With C, every single programmer has to properly account for memory safety in just about every program they write. If you fix a memory safety bug in one program, that's not necessarily going to help any other program.
With safe Rust, that work has largely been done for you. If a memory safety bug is found and fixed in Rust's standard library, it's found and fixed for every program that uses safe Rust (with a recompile using the new standard library version).
The point is for some experts to work together to get things as memory safe as possible in one place instead of everyone having to get things as memory safe as possible in every place.
Do you get a panic or a segfault?
And to the GP: yes, if your safe Rust program segfaults because of a bug in the Rust standard library, then you file a bug with the Rust project. I agree this is nice!
But in most real-world cases, your Rust program will segfault (or panic) due to a bug in your own code or in some code in one of its many dependencies. Then you also debug it.
But indeed you are in a good place if you manage to get a segfault. The much worse case is that the program misbehaves in some unexplainable way.
The problem is if the program misbehaves in some unexplainable way due to random corruption. And memory safety is the right path to reduce such problems. But only in the imaginary world of perfectly safe Rust code is this a reality, or with Fil-C, or verified C code, or some other memory safe language. In the actual world of most systems programming scenarious even with Rust it is only somewhat better than C.
The strategy when integrating unsafe segments to your Rust codebase is that you provide a safe interface for them, then very carefully review the code, and also use tools such as Miri to test them. Ideally your business side would not have any unsafe code in it.
In lieu of finding out that there are in fact multiple memory issues in std is at least amusing and some might say well deserved egg in the face of the more, shall we say, enthusiastic defenders of the language.
These haha gotcha's are criticism leveled at language designers who never denied that there is unsafe code, that there can be bugs in unsafe code, and that such bugs can leak to safe code.
egg in the face
Is it an egg in the face when it requires LLM analysis to find these nowadays? It tells me that the Rust developers got really good at eliminating memory bugs that are lurking in unsafe code.
Also, this doesn't change anything to the benefits of Rust's ownership model, which removes classes of bugs that are very common in C/C++, so the Rust advocates were still right.
(I'll leave my rants about Rust in a thread where it's more appropriate, but they are not about memory safety.)