It is also worth to note that the Rust design, in its theory, and the recent bug in the Linux kernel Rust code (the message passing abstraction used by Android), makes clear that:
1. With Rust, you may lower the exposure, but the same classes of bug still remain. And of course, all the other non memory related bugs.
2. With C you may, if you wish, develop a big sensibility to race conditions, and stay alert. In general it is possible that C programmers have their "bugs antenna" a bit more developed than other folks.
3. With Rust, to decrease the "unsafe" sections amount, you need often to build abstractions that may be a bit unnatural.
4. Rust may create a false sense of security, and in the unsafe sections the programmer sometimes, when reviewing the code, is falsely convinced by the mandatory SAFETY comment. Like in the Linux Kernel bug, where such comment was hallucinated by a human that sometimes (not sure in this specific case, it's just an example) may be less used to do the "race spotting" process that C learns you to do.
5. With Rust, in case of a bug, the fix could no longer be the one-liner usually you see in C fixes, and can make the exposure time window larger. Sometimes fixing things in Rust means refactoring in non trivial ways.
6. With C, if there was the same amount of effort in creating wrappers to make kernel programming safer at the cost of other things, the surface of attack could also be lowered in a significant way (see for instance Redis use of sds.c: how many direct strings / pointers manipulation we avoid? The same for other stuff of course). Basically things like sds.c let you put a big part of the unsafe business in a self contained library.
So, is Rust an interesting language for certain features it has? Yes. Is Rust a silver bullet? No. So should Rust be "pushed" to others, hell no, and I suggest you to reply in the most firm way to people stressing you out to adopt Rust at all the costs.
The recent bug in the Linux kernel Rust code, based on my understanding, was in unsafe code, and related to interop with C. So I wouldn't really classify it as a Rust bug. In fact, under normal circumstances (no interop), people rarely use unsafe in Rust, and the use is very isolated.
I think the idea of developers developing a "bugs antenna" is good in theory, though in practice the kernel, Redis, and many other projects suffer from these classes of bugs consistently. Additionally, that's why people use linters and code formatters even though developers can develop a sensitivity to coding conventions (in fact, these tools used to be unpopular in C-land). Trusting humans develop sensibility is just not enough.
Specifically, about the concurrency: Redis is (mostly) single-threaded, and I guess that's at least in part because of the difficulty of building safe, fast and highly-concurrent C applications (please correct me if I'm wrong).
Can people write safer C (e.g. by using sds.c and the likes)? For sure! Though we've been writing C for 50+ years at this point, at some point "people can just do X" is no longer a valid argument. As while we could, in fact we don't.
I hear "people rarely use unsafe rust" quite a lot, but every time I see a project or library with C-like performance, there's a _lot_ of unsafe code in there. Treating bugs in unsafe code as not being bugs in rust code is kind of silly, also.
Exactly. You don't need much unsafe if you use Rust to replace a Python project, for instance. If there is lower level code, high performances needs, things change.
For replacing a Python project with Rust, unsafe blocks will comprise 0% of your code. For replacing a C project with Rust, unsafe blocks will comprise about 5% of your code. The fact that the percentage is higher in the latter case doesn't change the fact that 95% of your codebase is just as safe as the Python project would be.
A big amount of C code does not do anything unsafe as well, it calls other stuff, do loops, logic business, and so forth. It is also wrong to believe 100% of the C code is basically unsafe.
If so, then it should be trivial for someone to introduce something like Rust's `unsafe` keyword in C such that the unsafe operations can be explicitly annotated and encapsulated.
Of course, it's not actually this trivial because what you're saying is incorrect. C is not equipped to enforce memory safety; even mundane C code is thoroughly suffused with operations that threaten to spiral off the rails into undefined behavior.
It is not so hard to introduce a "safe" keyword in C. I have a patched GCC that does it. The subset of the language which can be used safety is a bit too small to be full replacement on its own, but also not that small.
C lacks safe primitives or non-error-prone ways to build abstractions to refer to business objects. There are no safe string references, let along ways to safely manipulate strings. Want to iterate over or index into a result set? You can try to remember to put bounds checks into every API function.
But even with explicit bounds checks, C has an ace up its sleeve.
int cost_of_nth_item(int n) {
if (n < 0 || n >= num_items)
return -1; // error handling
…
}
Safe, right? Not so fast, because if the caller has a code path that forgets to initialize the argument, it’s UB.
You're swapping definitions of unsafe. Earlier you were referring to the `unsafe` keyword. Now you're using `unsafe` to refer to a property of code. This makes it easy to say things like "It is also wrong to believe 100% of the C code is basically unsafe" but you're just swapping definitions partway through the conversation.
What I see is that antirez claims that absence of "safe" (as syntax) in C lang doesn't automatically mean that all of C code is unsafe (as property). There's no swapping of definitions as I see it.
I think there's a very clear switch of usage happening. Maybe it's hard to see so I'll try to point out exactly where it happens and how you can spot it.
First from antirez:
> You don't need much unsafe if you use Rust to replace a Python project, for instance. If there is lower level code, high performances needs, things change.
Use of the term `unsafe` here referring to the keyword / "blocks" of code. Note that this statement would be nonsensical if talking about `unsafe` as a property of code, certainly it would be inconsistent with the later unsafe since later it's claimed that C code is not inherently "unsafe" (therefor Rust would not be inherently "unsafe").
Kibwen staying on that definition here:
> For replacing a Python project with Rust, unsafe blocks will comprise 0% of your code. For replacing a C project with Rust, unsafe blocks will comprise about 5% of your code.
Here is the switch:
> A big amount of C code does not do anything unsafe as well
Complete shift to "unsafe" as being a property of code, no longer talking about the keyword or about blocks of code. You can spot it by just rewriting the sentences to use Rust instead of C.
You can say:
"A big amount of 'unsafe' Rust code does not do anything unsafe as well"
"It is also wrong to believe 100% of the unsafe Rust code is basically unsafe."
I think that makes this conflation of terms clear, because we're now talking about the properties of the code within an "unsafe" block or globally in C. Note how clear it is in these sentences that the term `unsafe` is being swapped, we can see this by referring to "rust in unsafe blocks" explicitly.
This is just a change of definitions partway through the conversation.
p.s. @Dang can you remove my rate limit? It's been years, I'm a good boy now :)
High performance is not an on/off target. Safe rust really lets you express a lot of software patterns in a "zero-cost" way. Sure, there are a few patterns where you may need to touch unsafe, but safe rust itself is not slow by any means.
For your last sentence, I believe topics are conflated here.
Of course if one writes unsafe Rust and it leads to a CVE then that's on them. Who's denying that?
On the other hand, having to interact with the part of the landscape that's written in C mandates the use of the `unsafe` keyword and not everyone is ideally equipped to be careful.
I view the existence of `unsafe` as pragmatism; Rust never would have taken off without it. And if 5% of all Rust code is potentially unsafe, well, that's still much better than C where you can trivially introduce undefined behavior with many built-in constructs.
Obviously we can't fix everything in one fell swoop.
>>Of course if one writes unsafe Rust and it leads to a CVE then that's on them. >>Who's denying that?
>>The recent bug in the Linux kernel Rust code, based on my understanding, was >>in unsafe code, and related to interop with C. So I wouldn't really classify >>it as a Rust bug.
Why is glue code not normal code in Rust? I don't think anyone else would say that for any other language out there. Does it physically pain you to admit it's a bug in Rust code? I write bugs in all kind of languages and never feel the need for adjectives like "technical", "normal", "everyday" or words like "outlier" to make me feel not let down by the language of choice.
I have worked with Rust for ~3.5 years. I had to use the `unsafe` keyword, twice. In that context it's definitely not everyday code. Hence it's difficult to use that to gauge the language and the ecosystem.
Of course it's a bug in Rust code. It's just not a bug that you would have to protect against often in most workplaces. I probably would have allowed that bug easily because it's not something I stumble upon more than once a year, if even that.
To that effect, I don't believe it's fair to gauge the ecosystem by such statistical outliers. I make no excuses for the people who allowed the bug. This thread is a very good demonstration as to why: everything Rust-related is super closely scrutinized and immediately blown out of proportion.
As for the rest of your emotionally-loaded language -- get civil, please.
I don't care if there can be a bug in Rust code. It doesn't diminish the language for me. I don't appreciate mental gymnastics when evidence is readily available and your comments come out as compulsive defense of something nobody was really is attacking. I'm sorry for the jest in the comments.
I did latch onto semantics for a little time, that much is true, but you are making it look much worse than it is. And yes I get a PTSD and an eye-roll-syndrome from the constant close scrutiny of Rust even though I don't actively work with it for a while now. It gets tiring to read and many interpretations are dramatically negative for no reason than some imagined "Rust zealots always defending it" which I have not seen in a long time here on HN.
But you and me seem to be much closer in opinion and a stance than I thought. Thanks for clarifying that.
The bug in question is in rust glue code that interfaces with a C library. It's not in the rust-C interface or on the C side. If you write python glue code that interfaces with numpy and there's a bug in your glue, it's a python bug not a numpy bug.
I already agreed that technically it is indeed a bug in the Rust code. I would just contest that such a bug is representative is all. People in this thread seem way too eager to extrapolate which is not intellectually curious or fair.
There is a reason for this. A lot of libraries were written at a time when the Rust compiler either rejected sound and safe code so you have to reach for unsafe, or `core` didn't yet deliver safe abstractions.
In Rust you can avoid "unsafe" when you use Rust like it was Go or Python.
If you write low level code, that is where C is in theory replaceable only by Rust (and not by Go), then you find yourself in need of writing many unsafe sections. And to lower the amount of unsafe sections, you have to build unnatural abstractions, often, in order to group such unsafe sections into common patterns. Is is a tradeoff, not a silver bullet.
Not necessarily at all. Go peruse the `regex` crate source code, including its dependencies.
The biggest `unsafe` sections are probably for SIMD accelerated search. There's no "unnatural abstractions" there. Just a memmem-like interface.
There's some `unsafe` for eliding bounds checks in the main DFA search loops. No unnatural abstractions there either.
There's also some `unsafe` for some synchronization primitives for managing mutable scratch space to use during a search. A C library (e.g., PCRE2) makes the caller handle this. The `regex` crate does it for you. But not for unnatural reasons. To make using regexes simpler. There are lower level APIs that provide the control of C if you need it.
That's pretty much it. All told, this is a teeny tiny fraction of the code in the `regex` crate (and all of its dependencies).
I think this framing is a bit backwards. Many C programs (and many parts of C programs) would benefit from being more like Go or Python as evident by your very own sds.c.
Now, if what you're saying is that with super highly optimized sections of a codebase, or extremely specific circumstances (some kernel drivers) you'd need a bit of unsafe rust: then sure. Though all of a sudden you flipped the script, and the unsafe becomes the exception, not the rule; and you can keep those pieces of code contained. Similarly to how C programmers use inline assembly in some scenarios.
Funny enough, this is similar to something that Rust did the opposite of C, and is much better for it: immutable by default (let mut vs. const in C) and non-nullable by default (and even being able to define something as non-null).
Flipping the script so that GOOD is default and BAD is rare was a huge win.
I definitely don't think Rust is a silver bullet, though I'd definitely say it's at least a silver alloy bullet. At least when it comes to the above topics.
In my experience (several years of writing high performance rust code), there’s only really 2 instances where you need unsafe blocks:
- C interop
- Low level machine code (eg inline assembly)
Most programs don’t need to do either of those things. I think you could directly port redis to entirely safe rust, and it would be just as fast. (Though there will need be unsafe code somewhere to wrap epoll).
And even when you need a bit of unsafe, it’s usually a tiny minority of any given program.
I used to think you needed unsafe for custom container types, but now I write custom container types in purely safe rust on top of Vec. The code is simpler, and easier to debug. And I’m shocked to find performance has mostly improved as a result.
> was in unsafe code, and related to interop with C
1) "interop with C" is part of the fundamental requirements specification for any code running in the Linux kernel. If Rust can't handle that safely (not Rust "safe", but safely), it isn't appropriate for the job.
2) I believe the problem was related to the fact that Rust can't implement a doubly-linked list in safe code. This is a fundamental limitation, and again is an issue when the fundamental requirement for the task is to interface to data structures implemented as doubly-linked lists.
No matter how good a language is, if it doesn't have support for floating point types, it's not a good language for implementing math libraries. For most applications, the inability to safely express doubly-linked lists and difficulty in interfacing with C aren't fundamental problems - just don't use doubly-linked lists or interface with C code. (well, you still have to call system libraries, but these are slow-moving APIs that can be wrapped by Rust experts) For this particular example, however, C interop and doubly-linked lists are fundamental parts of the problem to be solved by the code.
If Rust is no less safe than C in such a regard, then what benefit is Rust providing that C could not? I am genuinely curious because OS development is not my forte. I assume the justification to implement Rust must be contingent on more than Rust just being 'newer = better', right?
The issue is unrelated to expressing linked lists, it's related to race conditions in the kernel, which is one of the hardest areas to get right.
This could have happened with no linked lists whatsoever. Kernel locks are notoriously difficult, even for Linus and other extremely experienced kernel devs.
I love rust, but C does make it a lot easier to make certain kinds of container types. Eg, intrusive lists are trivial in C but very awkward in rust. Even if you use unsafe, rust’s noalias requirement can make a lot of code much harder to implement correctly. I’ve concluded for myself (after a writing a lot of code and a lot of soul searching) that the best way to implement certain data structures is quite different in rust from how you would do the same thing in C. I don’t think this is a bad thing - they’re different languages. Of course the best way to solve a problem in languages X and Y are different.
And safe abstractions mean this stuff usually only matters if you’re implementing new, complex collection types. Like an ECS, b-tree, or Fenwick tree. Most code can just use the standard collection types. (Vec, HashMap, etc). And then you don’t have to think about any of this.
>> I guess that's at least in part because of the difficulty of building safe, fast and highly-concurrent C applications (please correct me if I'm wrong).
You wrote that question in a browser mostly written in C++ language, running on an OS most likely written in C language.
OS can be actually pretty simple to make. Sometimes it's a part of a CS curriculum to make one. If it were so much easier to do it in other languages (e.g. in Rust), don't you think we would already be using them?
Writing a real one? Who's gonna write all the drivers and the myriad other things?
And the claim was not that it's "so much easier", but that it is so much easier to write it in a secure way. Which claim is true. But it's still a complex and hard program.
(And don't even get started on browsers, it's no accident that even Microsoft dropped maintaining their own browser).
The toy one can still be as highly concurrent as the the real one. The amount of drivers written for it doesn't matter.
The point is if it were much easier, then they would overtake existing ones easily, just by adding features and iterating so much faster and that is clearly not the case.
>>difficulty of building safe, fast and highly-concurrent C
This was the original claim. The answer is, there is a tonne of C code out there that is safe, fast and concurrent. Isn't it logical? We have been using C for the last 50 years to build stuff with it and there is a lot of it. There doesn't seem to be a big jump in productivity with the newer generation of low level languages, even though they have many improvements over C.
This is anecdotal, I used to do a lot of low level C and C++ development. And C++ is a much bigger language then C. And honestly I don't think I was ever more productive with it. Maybe the code looked more organized and extendable, but it took the same or larger amount of time to write it. On the other hand when I develop with Javascript or C#, I'm easily 10 times more productive then I would be with either C or C++. This is a bit of apples and oranges comparison, but what I'm trying to say is that new low level languages don't bring huge gains in productivity.
> With C you may, if you wish, develop a big sensibility to race conditions, and stay alert. In general it is possible that C programmers have their "bugs antenna" a bit more developed than other folks.
I suppose it's possible. I wonder if I'll become a better driver if I take off my seatbelt. Or even better, if I take my son out of my car seat and just let him roam free in the back seat. I'm sure my wife will buy this.
Human behaviour can be a confounding thing. There was some debate a while ago [1] about whether bike helmet use may actually lead more head injuries due to factors like drivers passing closer to helmeted riders vs. unhelmeted ones or riders riding more recklessly, among a tonne of other factors. I still prefer to wear a helmet, but its an interesting example of how difficult it can be to engineer human behaviour.
Another good example of this is how civil engineers adding safety factors into design of roads - lane widths, straighter curves, and so on - leading drivers to speed more and decreasing road safety overall.
FWIW, FAFO is a very good way to learn. Assuming we can respawn indefinitely and preserve knowledge between respawns, driving fast and taking off your seatbelt would definitely teach you more than just reading a book.
But in this specific case, if the respawn feature is not available or dying isn't a desirable event, FAFO might not be the best way to learn how to drive.
I also think we have the data in for memory safety in C. Even the best people, with the best processes in the world seem to keep writing memory safety bugs. The “just be more vigilant” plan doesn’t seem to work.
> FWIW, FAFO is a very good way to learn. Assuming we can respawn indefinitely and preserve knowledge between respawns, driving fast and taking off your seatbelt would definitely teach you more than just reading a book.
Yes, just sucks for the person who you hit with your car, or the person whose laptop gets owned because of your code.
"FAFO" is not a great method of learning when the cost is externalized.
> With C you may, if you wish, develop a big sensibility to race conditions, and stay alert. In general it is possible that C programmers have their "bugs antenna" a bit more developed than other folks.
I think there are effects in both directions here. In C you get burned, and the pain is memorable. In Rust you get forced into safe patterns immediately. I could believe that someone who has done only Rust might be missing that "healthy paranoia". But for teaching in general, it's hard to beat frequent and immediate feedback. Anecdotally it's common for experienced C programmers to learn about some of the rules only late in their careers, maybe because they didn't happen to get burned by a particular rule earlier.
> Rust may create a false sense of security, and in the unsafe sections the programmer sometimes, when reviewing the code, is falsely convinced by the mandatory SAFETY comment.
This is an interesting contrast to the previous case. If you write a lot of unsafe Rust, you will eventually get burned. If you're lucky, it'll be a Miri failure. I think this makes folks who work with unsafe Rust extremely paranoid. It's also easier to sustain an that level of paranoia with Rust, because you hopefully only have to consider small bits of unsafe code in isolation, and not thousands of lines of application logic manipulating raw pointers or whatever.
The amount of paranoia I need for unsafe Rust is orders of magnitudes higher than C. Keeping track of the many things that can implicity drop values and/or free memory, and figuring out if im handling raw pointers and reference conversions in a way that doesn't accidentally alias is painful. The C rules are fewer and simpler, and are also well known, and are aleviated and documented by guidelines like MISRA. Unsafe Rust has more rules, which seem underspecified and underdocumented, and also unstable. Known unknowns are preferable over unknown unknowns.
A quick unscientific count on cve.org counts ~86 race condition CVEs in the Linux kernel last year, so you might be overstating how well bug antennas work.
If the kernel was completely written in Rust, we could have a lot of unsafe places, and many Rust CVEs. It is hard to tell, and the comparison in theory should be made after the kernel is developed only by people lacking the C experience that made the current developers so able to reason about race conditions (also when they write Rust).
That's quite the double standard. You extrapolate from one single Rust bug, but insist that "it's hard to tell" and you need completely unrealistic levels of empirical evidence to draw conclusions from the reported C bugs...
Yeah I mean I could also say "there are no CVEs written in PERL in the kernel ergo PERL is safer to write than Rust". Given there's close to zero .pl files in the kernel, I think we can all agree my assertion holds
That claim relies on an absurd "in the kernel" qualifier, making it difficult to agree with. Furthermore, your hypothesis is that "we all" agree with claims that rely on absurd conditions as a matter of course.
(2) and (3) just don't seem to be the case empirically. One bug that was directly in a grep'able `unsafe` block is hardly evidence of these, whereas Google's study on Rust has demonstrated (far more rigorously imo) the opposite. I think anyone paying attention would have guessed that the first Rust CVE would be a race - it is notoriously hard to get locking/ race semantics correct in the kernel, not even the C programmers get it right, it's an extremely common bug class and I believe Linus has basically said something along the lines of "no one understands it" (paraphrasing).
(4) Again, this doesn't seem to be borne out empirically.
(5) I've seen plenty of patches to C code that are way more than a single line for the Linux kernel, but sure, maybe we grant that a bug fix in Rust requires more LOC changed? It'd be nice to see evidence. Is the concern here that this will delay patching? That seems unlikely.
It's not uncommon at all for patches to the C code in the kernel for "make this generally safe" are 1000s of lines of code, seeding things like a "length" value through code, and take years to complete. I don't think it's fair to compare these sorts of "make the abstraction safe" vs "add a single line check" fixes.
(6) Also not borne out. Literally billions spent on this.
> So, is Rust an interesting language for certain features it has? Yes. Is Rust a silver bullet? No.
Agreed. I'm tempted to say that virtually no one contests the latter lol
> So should Rust be "pushed" to others, hell no, and I suggest you to reply in the most firm way to people stressing you out to adopt Rust at all the costs.
I guess? You can write whatever you want however you want, but users who are convinced that Rust code will provide a better product will ask for it, and you can provide your reasoning (as SQLite does here, very well imo) as firm as you'd please I think.
> made the current developers so able to reason about race conditions (also when they write Rust).
Aha. What? Where'd you get this from? Definitely not from Linus, who has repeatedly stated that lock issues are extremely hard to detect ahead of time.
> we’ve tweaked all the in-kernel locking over decades [..] and even people who know what they are doing tend to get it wrong several times
Google have published a couple high-level Rust blog posts with many graphs and claims, but no raw data or proofs, so they haven’t demonstrated anything.
By now their claims keep popping up in Rust discussion threads without any critical evaluation, so this whole communication is better understood as a marketing effort and not a technical analysis.
> Google have published a couple high-level Rust blog posts with many graphs and claims, but no raw data or proofs, so they haven’t demonstrated anything.
Don't expect proofs from empirical data. What we have is evidence. Google has published far better evidence, in my view, than "we have this one CVE, here are a bunch of extrapolations".
> By now their claims keep popping up in Rust discussion threads without any critical evaluation,
Irrelevant to me unless you're claiming that I haven't critically evaluated the information for some reason.
1. With Rust, you may lower the exposure, but the same classes of bug still remain. And of course, all the other non memory related bugs.
2. With C you may, if you wish, develop a big sensibility to race conditions, and stay alert. In general it is possible that C programmers have their "bugs antenna" a bit more developed than other folks.
3. With Rust, to decrease the "unsafe" sections amount, you need often to build abstractions that may be a bit unnatural.
4. Rust may create a false sense of security, and in the unsafe sections the programmer sometimes, when reviewing the code, is falsely convinced by the mandatory SAFETY comment. Like in the Linux Kernel bug, where such comment was hallucinated by a human that sometimes (not sure in this specific case, it's just an example) may be less used to do the "race spotting" process that C learns you to do.
5. With Rust, in case of a bug, the fix could no longer be the one-liner usually you see in C fixes, and can make the exposure time window larger. Sometimes fixing things in Rust means refactoring in non trivial ways.
6. With C, if there was the same amount of effort in creating wrappers to make kernel programming safer at the cost of other things, the surface of attack could also be lowered in a significant way (see for instance Redis use of sds.c: how many direct strings / pointers manipulation we avoid? The same for other stuff of course). Basically things like sds.c let you put a big part of the unsafe business in a self contained library.
So, is Rust an interesting language for certain features it has? Yes. Is Rust a silver bullet? No. So should Rust be "pushed" to others, hell no, and I suggest you to reply in the most firm way to people stressing you out to adopt Rust at all the costs.