Hacker News new | ask | show | jobs
"No way to prevent this" say users of only language where this regularly happens (xeiaso.net)
208 points by tbillington 756 days ago
16 comments

Just in case someone needs the reference, the onion uses '"no way to prevent this" says only nation where this regularly happens' as a reoccurring article at every major school shooting[0], to highlight the frequency of such events and the fact that nothing has really changed since the last one.

[0] https://www.theonion.com/no-way-to-prevent-this-says-only-na...

I find the Wikipedia article also a good summary of the "joke" and is quite chilling to read.

https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...

This is also an ongoing series on that blog, if you look at previous entries there's lots with the same title, also about C having buffer overflows
Kudos for faithfully reproducing the joke then.
Thanks, I've had thoughts about making it a bit more elaborate (possibly involving large language models somehow to help synthesize what's going on into the right format, and so that it's not literally the same thing every time), but there's a charm in making it literally the same thing every time with the details swapped out. It points out the repetition to the level that all you need to do is swap out the details next time. This is all so preventable, but sometimes these things just happen and there's not anything anyone can do about them.
Didn't catch that, thanks!
The oversimplification is part of the joke.
I've been doing programming for ~31 years in total and ~22 years professionally and at this point I have lost all hope that programmers at large will ever gain these mythic qualities called "self-reflection" and "introspection".

Truth is, these people are simply afraid for their cozy jobs, that's all there is to it. Derivative states of mind like Stockholm Syndrome and Sunk Cost Fallacy are quite normal to appear in these conditions.

On OP: I could not agree more. People always downplay their fuck-ups, that's sadly part of being a Homo Sapiens, but the lack of awareness is still both despairing and hilarious to watch.

And finally, C/C++'s niches have decreased but these people will not adapt, of course. Almost anything I've done with those languages 15-20 years can today be done with Rust. Or if you are on a tight time budget -- Golang, and you still won't lose too much speed.

But sure, "nothing can be done, these things sometimes happen". Sigh.

I would love to be able to use Rust in my professional project. Unfortunately, I am doing high performance scientific computing. Rust doesn't even come close to offer any good alternative to cross-plateform, cross-device (CPU/GPU) libraries such as OpenMP Target, Kokkos, SYCL, ... I believe we need Nvidia/AMD to take Rust seriously (I'm not sure it is even possible without unsafe everywhere) to be able to offer good libraries.

In my world, using C++ is the modern language, because most project are stuck with Fortran.

That's completely valid and that's why I said "almost anything" -- other things include kernel development and embedded.

My broader point was that C/C++ are still kings in some areas but people insist on using them where a bunch of languages do better today.

Also cross-platform libraries, if you want to be everywhere, from computers and phones to set top boxes to weird Japanese rtoses you haven't even heard of, Rust just won't cut it.
Fortunately there are other languages that are more available cross-platform than Rust with its tiny Tier 1 list.

Unless ofc you're contractually obligated to use vendors half-arsed kludget together patchy version of GCC of unknown provenance that they compiled with barely half C support, let alone other GCC languages.

At least C++ provides the tools to avoid this kind of problems, if one choses to do so, instead of insisting into C idioms.
Yeah, part of the problem here is that the source code and the bug were in C, and people commenting here think C and C++ are the same thing.

There's a huge difference between C and C++. There's a big difference between 2001 version of C++ and 2021 version of C++.

The languages are so different that, in general, something that happens to one of them doesn't apply to the other.

I program MCUs as a hobby. I wish I could use something better than C. Even C++ with RAII would be better. But somehow, even C++ support is spotty, with unsupported features, broken debugging, etc. for seemingly no reason. And all device-specific libraries are written in C, so I'd have to write C++ wrappers for every little thing. Send help...
That's why I said "almost anything". I am aware that f.ex. kernel development and embedded work is still firmly in the C domain.

I am not evangelizing for Rust, Golang or anything in particular. I am evangelizing for periodically asking yourself "Am I still using the best tool for the job?" which most people never do.

Actually, maybe Rust would get more people interested if it didn't sound like a religion...
It’s a cult, but you don’t have to join the cult. I use Rust both privately and more and more frequently professionally and I don’t really notice the cult community outside of smiling at it once in a while when it blows up on HN.

We’re mostly adopting rust to replace our C, to protect us from ourselves. It’s just so much easier to hand off these sort of projects to developers who mainly work with things like Java or C#. Partly because of the memory “safety” but also because things like enums work the way they expect them to being sum types, and so on. It’s also very clear when something is intended not to be mutable. The borrow checker is probably the biggest struggle, where with C, everything is a struggle.

Rust still has a long way to come though, and maybe it’ll never get there. I do think we’ll see an uptake as performance increasingly matters due to cloud costs and ESG. Right now though, I’d argue that unless you know why you “want” to use Rust then maybe you shouldn’t.

I saw your comment on an older C# thread in which you said (paraphrased) that C# is not bad, has nothing really stellar, and is just kind of bland.

If you could theoretically swap out the entire back-end of a programming language, and hypothetically you did that to C# by allowing developers to write C# styling and syntax that could be compiled to Rust, it would probably not be a big loss. The type primitives would be an apples-apples comparison at best, or "Rust is just better" if you really scrutinized the types closely. Where does that leave the rest of the language? A better regex implementation? Rust would probably win. A better GUI experience? Rust is speedily approaching usable APIs there at the same time Microsoft is trying to sunset the best they had.

I wonder if F# could be rebased on top of Rust...

Mostly agreed with your comment though I still have to remark that every community has zealots and it's mystifying to me why are people so annoyed by Rust's. So it's hard to agree that "Rust is a cult".

As you said, it's a pragmatic but also kinda niche language. I don't reach for it unless I can't do the job with others, easier and quicker to work with languages.

Maybe it's you and some other curmudgeons projecting -- worth to consider if that's the case.

Maybe it's normal for people to praise something that legitimately solved their problems. I know that happened with me.

There is a difference between praising and preaching, the latter happens more often with rust
Mikroe is still in business selling Pascal and Basic compilers for all kinds of tiny CPU and MCUs since 1997, so some people do value their products.
Rust only works for a few popular targets, but if it is just a hobby then Zig might be an option. C interop is pretty seamless, so you don't face the problem of having to reinvent the universe.
In my experience interop is rarely a language problem, lion share of time is consumed by reading the docs, testing if you understood the docs correctly, testing what's implied but not written in the docs and figuring out how to cope with idiosyncratic interface.
No, more people should be using memory managed languages like Java and Golang. Then educate and expose people to sum types and pattern matching. It is actually insane how people is still unconvinced about sum types. Imagine how insane it is to program without product types? That's exactly what it felt like.
> It is actually insane how people is still unconvinced about sum types.

Oh don't even get me started on this but I agree sooooo much.

> No, more people should be using memory managed languages like Java and Golang.

Well, I use both Golang and Rust depending on the need. We can have both, it's not an either-or.

I'm feeling my lack of academic training here, but I needed to make sure. When you say sum types, are you referring to enums, or is it a broader concept than that? The Wikipedia article was quite archaic but seemed to support my initial understanding that you're talking about enums.

Are you talking about enums?

Rust's enum is a sum type. The enum in C is just integers (again) wearing a hat for some reason. This may not be obvious, but if my enum in C seemingly has three possible values it... doesn't, it actually has at least hundreds of possible values, but only three have names - it's just an integer in a hat.

A sum type is an idea from type arithmetic. Suppose we have two types A and B, and we want to produce a new type from those, the most common provision in languages is to let you make a product type, it has all the values of A multiplied by all the values of B, this may be available to you as a named tuple, perhaps named a "struct" or "class" in your preferred language.

But what if instead we added these types instead of multiplying them? The new type has the values of A plus the values of B ? That's a sum type.

Suppose I have three types which resemble booleans: mood (it's either happy or sad), size (huge or tiny) and activity (either sleeping or eating)

A sum type of these three would allow me to make a "brief" of a cat as one of the six values, either as its mood, its size or its activity. The cat brief can be sad, or sleeping, or tiny, but it cannot be sleeping and sad [in this model] any more than a cat's size could be both huge and tiny. There are 2 + 2 + 2 = six values of the brief type.

In contrast a product type, maybe lets call that "state" of the cat consists of separate values of each of the three constituents, so the state of a cat might be (sad, tiny, sleeping) or (happy, huge, eating) or any other combination. There are 2 * 2 * 2 = eight values of this state type.

Just as an arithmetic is kinda crap if it only has multiply but not add, a type system is kinda crap if it can't make sum types. Where complicated hard-to-get-right hacks are used (e.g. C++ std::variant) these are often unsatisfying both ergonomically and in terms of delivered functionality, so hence the desire to have actual sum types in the language.

In Rust they are called "enum", elsewhere "tagged union", "discriminated union" or "variant" (under the hood, all of those are just a C-style union bundled with a tag field which identifies the active content of the union). The rest is language specific syntax sugar which makes their usage more or less convenient and typesafe (and sometimes more memory efficient, for instance a pointer|null type doesn't need the tag since the set of valid pointers doesn't overlap with null)
> (under the hood, all of those are just a C-style union bundled with a tag field which identifies the active content of the union)

Crucially that's not necessarily what's going on in Rust and (assuming it lands) won't be the actual implementation of C++ std::optional<T&> specialisation.

Instead the language can decide from the value which type it has. This is where Rust's Guaranteed Niche Optimisation kicks in. If we've promised the compiler that type T's values don't occupy the space needed fully, the GNO promises that sum types consisting of T plus one other value are the same size as T was. There is no "tag field".

In practice, the compiler can provide optimisations beyond the Guarantee. For example the compiler can see that OwnedFd (a file descriptor) has a niche so Option<OwnedFd> is the same size as OwnedFd (ie the same size as a C integer, typically 4 bytes), but it can also see that char has a huge niche. Many of the 32 bits aren't needed to store a 21-bit Unicode scalar value, so (although the guarantee doesn't apply) a sum type of a char and six hundred other named possibilities is the same size as just the char was.

Ah, I see you mention this at the end, I think it's worth highlighting much earlier, that the "tag" isn't actually a mechanically necessary part of such a type.

Yes, I am talking Rust enums and OCaml's enumerated data types.
Or what F# calls a Discriminated Union.
Just call them "tagged union" or "variant" and suddenly "sum types" are not so rare anymore ;)

...same with "struct", "record" or "tuple" vs "product type". Nobody calls them that except functional programmers and mathematicians, both quite rare specimen in the wider programming world, but nevertheless they are ancient and widely known concepts even among us "peasant coders" ;)

> Just call them "tagged union" or "variant" and suddenly "sum types" are not so rare anymore ;)

Nah, this is a false dichotomy. If a language doesn't have product types but instead resort to declaring variables separately, you don't call that "structs". Similarly, calling tagged unions a sum type is kind of misleading. They are both programming languages, I am sure I can do whatever I can do in A using B.

Why is a "struct" such a powerful concept? Because it is correct by construction. When I declare struct A, I have everything that A should contain. It is impossible to say, oops, I forgot about A::b. Similarly, a sum type as a concept is only useful when the abstraction is very, very solid.

Tagged unions or variants (in C and C++ respectively) is nothing like that. I have a variant A, I checked that that it is B, but oops, I casted it as C. Its your typical TOCTOU (or LOCLOU for line of check, line of use, I guess). std::optional is also like, same with pointers. Proper sum types, like Rust, it is literally impossible for me to get C by mistake.

Obviously, all of this minus some underlying implementation details, unsafe code yadda yadda.

Sorry if I sound aggressive, but I am just tired of "we have A at home, A at home (the most cursed shit)".

Does go have enums? Yea... just declare some global constants like so bro.

SomeEnum_A = 0

SomeEnum_B = 1

SomeEnum_C = 2

Yea..."enums"..

Edit: Oh how can I forget, we had this kind of issue just TODAY in production. No wonder I am so pissed off.

It's possible to write systematically safe code in C, no need to change language. People really just don't want to do it.
It's possible to write systematically safe code in assembly, no need to use any language. People really just don't want to do it.
When the language has builtin safety, it has better ergonomics, so there's merit in switching language.
If that language you speak of is Rust or many GC more dynamic languages I'll agree. If you mean C/C++, frak no. They don't have "builtin safety" at all. Nothing that's opt-in is safe or secure.
You're entitled to your opinion, the universe has an opinion too.
In theory, in 50 years of practice, not really.
If you mean those two godlike C programmers, that's not what I mean.
Maybe it is but the last several years of periodic (and quite embarrassing) CVEs suggest otherwise.
What they suggest? What I and the discussed article claim is that safe code isn't written because programmers don't want it. CVEs aren't caused by desires, they are caused by unsafe code.
Many people suggest to start gradually migrating to either Rust or start investing much more seriously in formally proving C/C++ code (which is IMO a huge endeavor).

As I said in multiple other comments, I know there are valid cases for C/C++ where various factors prevent migration. I am not playing a little rebel revolutionary here, I am addressing the people who can migrate away but refuse to do so based on hand-wavy philosophy clashes (or my favorite petty rebellious take: "people praise Rust, I must resist using it!"). Petty stuff and I am pretty disappointed that people who bill themselves as "engineers" refuse to see objective evidence and hold on to only what they know.

Those are the people I address with my comment. Not the people who will be never allowed to migrate away a 2 million lines worth of C codebase. They have my deepest sympathies.

I didn't propose to migrate to rust.
> Petty stuff and I am pretty disappointed that people who bill themselves as "engineers" refuse to see objective evidence and hold on to only what they know.

I think it is widely accepted that Rust is more safe. (There comes the NPM factor too so it's not entirely clear). The thing is just, you are excited about this technology, and you are equating "engineering" with "security". You are willing to pretend that other merits and factors don't exist, including ergonomics of iteration, or inertia. I tried writing my usual explorative Win32 code in Rust for an evening. Well, it was painful and I went back to do it the way that works for me, that is supported by all the official tooling, and that the official documentation is written in. I also can't see myself reading reasonable Rust code quicker than I can read and write reasonable C code in a year down the line. It's just too intricate, too clever, too condensed / abstracted and at the same time too verbose. Another time I went to download a couple simple projects (e.g. text editor) to dabble a bit with. But the stuff was too opaque and had _hundreds_ of third-party dependencies, and I couldn't understand it well. So I lost interest.

Choice of language and ecosystem is an economic matter. They get chosen based on what one knows and what one wants and how one evaluates the possibilities. "Security" quite frankly is not the most important of concerns in most situations, and for a good reason. I frankly am not getting paid in finding the nicest or "most secure" way to write a piece of code, but to get it done. I am not interested in following the development of the hundreds of dependencies of my Rust project, and to change my data structures when they learned a better way to design the API to those "safe" data structures.

But you are seeing only "security". Well, it's most secure to just shut your computer off. So much for "objectivity". Maybe you _are_ the zealot!

THIS, sadly. Though it'd be more accurate to re-phrase: 's/just don't want to do it/have other priorities, or are stuck in organizations with other priorities/'
I get that we can't always do what we want in our jobs (and in some places quite rarely even). I get it fully and I sympathize.

But that still doesn't do C/C++ any favors. None of my Rust contracting work ever resulted in a buffer overflow.

And it doesn't need to be Rust, as the anti-seat belt folks always are quick to complain about, plenty of systems languages since PL/I and NEWP days have bounds checks by default.

Back in the days we were arguing for Ada, Modula-2, Object Pascal on Usenet, we were straitjacket programming advocates apparently.

Haha, nice analogy, thank you. :)

And yeah I am gradually giving up on arguing with people who are extremely biased but point at me and blame me for being biased. Seems there's no win.

I actually regret engaging so thoroughly in this thread but once started I figured I'll not budge and will hold my ground. I am anti-group-think that way.

I haven't engaged in such threads in a while and I think the next time around is going to be months in the future. We'll see.

Eh, Rust would be fine if not for the fact that it's too opinionated.

Unfortunately you can't just have Rust's safety checks, without opting into restrictions that Rust designers force onto You that aren't inherent to safety checks, but more because 'that's a better practice (according to us)'.

And also, easy and fast iteration just isn't there, both because of borrow checker restrictions and compile times

C/C++ being non-opinionated is the main source of the security vulnerabilities.

Let's face it, it felt good to be a lone cowboy carrying a lot of responsibility and knowing what you are doing. I was there myself and I'll admit the ego trip was awesome.

These times are long past and naturally, people refuse to adapt.

> Unfortunately you can't just have Rust's safety checks, without opting into restrictions that Rust designers force onto You that aren't inherent to safety checks, but more because 'that's a better practice (according to us)'.

Show me something that does better and I'll switch tomorrow. But don't tell me C/C++ are better -- they are not. Too much freedom leads to CVEs literally every month somewhere and that's only because we don't have better vetting and checking tools, otherwise I'm sure we'd be getting one every day for a while.

> And also, easy and fast iteration just isn't there, both because of borrow checker restrictions and compile times

I agree on that, that's why I mentioned Golang. Most of the C/C++ systems I worked on around 15-20 years ago didn't need the close-to-the-metal speed because at least 90% of their time was spent on I/O... frak, even Python would have done well there. And Golang is times faster. It's a very nice compromise if you want to be productive and don't care super much about CPU speed efficiency.

> C/C++ being non-opinionated is the main source of the security vulnerabilities

This. "Undefined behavior" is such a terrible way of thinking. As is the "we can assume in the optimizer that UB does not happen and then eliminate code on that basis", which allows the compiler to introduce bugs that only appear at certain -O levels.

It took decades to get them to define arithmetic as twos-complement.

> It took decades to get them to define arithmetic as twos-complement.

I'm not sure this is right? IIRC C++20/C23 require two's complement representation for signed integers but generally leave other behaviors (including signed overflow) the same.

[0]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p09...

I’d say the biggest problem of interpreted, JIT compiled, and GCed languages is not the speed; it’s the RAM use. I agree that quite often we don’t need the speed of close-to-the-metal. But there is something wrong with most programs eating tens or hundreds of megabytes, without doing much.
And I agree on that. My favorite Elixir's runtime (Erlang's BEAM VM) still has some subtle problems with holding on to big(ger) binaries (strings) that requires very specific code be written at the green thread boundaries and it can get pretty maddening if you don't get it right -- which is not easy.

Golang I hear is doing much better btw. But you still have to be wary of its footguns i.e. leaking goroutines.

Go is doing much better on memory use because 1) it is AOT compiled, 2) it tries to allocate as much as possible on the stack (escape analysis), 3) it supports value types (the items of an array and the fields of a struct are contiguous in memory). But it's still a GCed language, which generally means a slightly higher memory use, controlled by $GOGC, to not spend too much time CPU time on garbage collection. Overall, Go is an excellent tradeoff for most applications.
There are restrictions out on You by the borrow checker to ensure safety, and then there are restrictions put on You by rust design team 'just because'.

Again, the former are fine, it's the latter I have a problem with.

In order for me to agree on the "just because" part you'll have to give some examples. What made you think they are arbitrary? And how did they prevent you from doing your job?
Comment above, mentioned borrowing while structs instead of borrowing memory. I believe this was once discussed under term 'partial borrows', but the "idiomatic" aproach is to 'just split your structs'.

Which isn't really a good aproach to structuring codebase, it's just to appeal to borrow checker inflexibility.

Lack of global scope. Lack of function overloading.

I would say until C++98, C++ used to be more opinated, one of the reasons many of us went with C++ when given the choice, wasn't OOP features, rather the security improvements over bare bones C, with compiler provider frameworks.

Then eventually C++ got invaded by C expatriates, and writing C with C++ compiler idioms increased instead of going away.

It is like giving Typescript to groups of folks that insist on using any all over the place.

Actually I have good-ish memories of the early `boost` (we're talking 2003 - 2006) and some of the `std::` libraries in C++. They got the job done fine and were not in the way. So yeah, agreed.
>Show me something that does better and I'll switch tomorrow.

There's no limit to perfection, but if you merely don't write C of opportunistic kind, logical errors quickly start to outweigh other types of errors.

Are you willing to die on this hill? I remember an HN post a while ago where both Microsoft and Google said something like 65% of the bugs in C code were related to memory (un)safety.
I don't deny that.
> Show me something that does better and I'll switch tomorrow

Frankly this is a very bad decision. Because now you have C code, Rust code, and yet another language's code, and you're left with a mess that you have to integrate too.

Obviously I was demonstrating that I don't shill for Rust in particular -- I simply believe C/C++ are not cutting it anymore. Rust is not perfect but it solves a sizeable chunk of their problems, that's all.
Most programmers don't have any opinions, so if they use an unopinionated language, they end up using patterns that opinionated people use. So it's better to have one source of truth for opinions, so that we don't end up using the "wrong" opinions of people who talk more than they think.
Compiler/language people are also just "opinionated people" though. Some have good opinions, some have bad opinions. In the worst case you have opinions which are the result of a 'design by committee/community' process.
Single source of truth of opinions can be challenged and changed if one's arguments are good enough. The benefit is that it's easier to see the advantages and disadvantages for those opinions, because they're all in the same place.

This is in contrast to C++, where one organization creates a set of guidelines, another organization creates another set of guidelines. Valid arguments of critique in one organization are not seen in the other.

Single source of truth is also beneficial to compiler authors as well, because they get more feedback how the language is used and why.

Unfortunately you have to pick 2 out of:

- Lack of restrictions

- Safety

- Performance

If you choose safety and no restriction, you pay the price in performance (for GC etc.)

There's a massive Terra Incognita to explore between Rust on one side and Python on the other side (just to pick two extremes).

It's not "2 out of 3", it's a triangle where a language can pick a sweet spot anywhere within the triangle (and ideally, it's not a "sweet spot" either, but more like a "sweet area" where the programmer can pick an actual spot within that area defined by the language).

That sort of extreme flexibility simply does not exist. I mean it does but then you are firmly in the dynamic languages territory and you are forgoing any hope for close-to-the-metal performance.
IMHO it does and its not restricted to dynamic languages (like JS or Python), look at this Zig function signature for instance (just an example from my current dabbling):

   fn setData(comptime pins: anytype, bus: anytype, data: u8) @TypeOf(bus)
In practice this looks and feels like dynamic typing, yet when looking at the compiler output it still resolves to optimal code (since it's "compile-time dynamic typing" not "run-time dynamic typing", but the difference in practice is surprisingly small).
Again, restrictions that are forced you for a price of safety are one thing. But what I'm complaining about are restrictions that don't have to be there to get borrowchecker working, but rather are there because designers arbitrary decided "it's better this way".
As someone just starting to finally learn Rust, I'm curious what some examples of this might be.
All AFAIK, since I only dabble occasionally in Rust:

The borrow checker works on "struct granularity", but it would be much more flexible and convenient if borrowing would work on memory location granularity (for instance passing a struct reference into a function "taints" the entire struct as borrowed, even if that function only accesses a single item in the borrowed struct - this 'coarse borrowing' restriction then may lead to all sorts of workarounds to appease the compiler, from 'restructuring' your structs into smaller pieces (which then however may fit one borrowing situation, but not another), or using 'semantic crutches' like Rc, Cell or Box.

There are also related restrictions about function call barriers. AFAIK the Rust compiler cannot "peek into" called function bodies to figure out what's actually going on inside those functions (and that information would be very valuable for fine-grained borrow checking), it can only work with the information in the function signature.

Again, disclaimer: take this with a grain of salt since I'm not a daily Rust user, but this is how I understood why Rust feels so restrictive.

So a few things .. Rust is a very good safe language. It also has an unsafe keyword to make the compiler ignore borrows etc.

If you want fast iteration, use python and then hand transpile your code into rust.

Not every tool has to be used at once ... but Python for the idea and rust for the implementation can be the best of both worlds ... :)

Except Rust's unsafe is even worse then any other 'unsafe' languages.

And while you can technically prototype in a other language, speed of iteration is always a bottleneck.

The only way to escape it is if You are an about master of language, the project you are working on and even the feature you are adding.

But that's a verry rare case scenario

I don't disagree btw. As a contractor I have found myself very often in a situation where Rust's slower iteration simply didn't work for me so I got back to Elixir and also relearned and started getting proficient in Golang.

Rust's slower iteration only disappears when you become a pro as you said and that's my main problem with it. I simply can't invest as much time and effort for free.

Rust is just a different language. It's not C-like or Java-like plus checks or anything like that.

It's a value oriented language. Variables mean something completely different than in any other common language. Everything is about the values and where they are kept at the moment.

This is one possibility. Have you considered the other, that you're the old-man-yells-at-cloud variety of narcissist?

Hence why you choose to interpret things as everyone (except you) is defective. Everyone (except you) is laughable, completely lacking in self-awareness, refusing to adapt and be and do as they should (according to you of course).

Because they're all lesser than you! That's why. You write Rust. So obviously, you're so far above them. Like a God looking down at monkeys.

Couldn't be reasons like inertia, or lacking the time, or not having the budget to change, or being too tired to learn, or any other such thing that'd let them be equal to you but just with differing priorities.

No, because then you wouldn't get your narcissistic supply, allowing your delusion you're better than others, rather than a man distracting himself from his own failings?

Just an alternative theory. What do you think?

Don't worry, HN went through this many times before. It was Go a few years ago, Node.JS before that. Rust too shall pass.
> Couldn't be reasons like inertia, or lacking the time, or not having the budget to change, or being too tired to learn, or any other such thing that'd let them be equal to you but just with differing priorities.

This is already included in the "almost anything" expression and you are the 4th person deliberately (or accidentally?) not noticing it.

There are valid cases for C/C++ both on technical merits and business specifics. I have not denied that.

---

The rest of your comment is just projection borne out of your faulty assumptions about my comment and it's thus not interesting at all.

> This is already included in the "almost anything" expression and you are the 4th person deliberately (or accidentally?) not noticing it.

I genuinely think that :

1/ this field is dominated by a disproportionate amount of people with autistic traits

2/ their ability to reason only functions in the narrowest sense and their grasp of language (takes everything in the most literal sense, thinks in extremes, prone to putting things in boxes, have a very, very strong attachment to their routine and are unable to ever leave the comfort zone they constructed) makes any attempt at communication beyond painful.

I have come to not even bother replying to people who are unable to understand human words such as when they interpret "most" as meaning "all" or "rarely" as "never" as it's one of the telltale signs that it is going to be extremely unproductive.

This phenomenon is the cause of most pains and drama. Once you start to see how this pattern develops you understand the true cause of the endless bikeshedding, of why even the idea of having a code of conduct can raise endless anger and storms and so on. You're touching a comfort zone so the temper tantrums are thrown. The routine has been built and it must continue until the end of times.

If there's any field in this world that is in need of more neurotypical, emotionally stable adults, this is it.

Or maybe, just maybe they correctly interpreted "most" as "more than half" and "rarely" as "decidedly less than half", and actually disagree with the statement as is. And what can I say, "reasons like inertia, or lacking the time, or not having the budget to change, or being too tired to learn, or any other such thing" sounds exactly what almost anybody is confronted with -- so it doesn't exactly seem like just an _exception_ to the set of "almost anything".

But it's nice to add words like "autistic", "thinking in extremes", "painful", etc. to terminally bash on those people who are wrong and just can't learn...

Or maybe, just maybe, I explicitly agreed with the real-world limitations up-thread but now you are just looking to pick a fight because me agreeing with stuff does not fit your preconceived notion. :)

But, by all means, go on, I can only presume you are bored.

Maybe C programmers need some more thoughts and prayers at deployment time?
You obviously need to perform the correct rites and pay your homage to the blessed machine spirit or the Omnissiah will not permit your code to compile.
We can learn a lot from terry davis
No, the only way to stop a bad dev with a strcpy() is a good dev with a strcpy().
C doesn't overflow and spill your memory contents, your RAM modules do!
Basically fuzzing then?
But the 2nd amendment guarantees their right to insecure code!
More broadly:

> "No way to prevent $THIS" say users of only language where $THIS regularly happens

A weird psychological quirk I've noticed (of myself, and others) is we'll often exhibit a sort of 'programming language xenophobia', where we apathetically accept (or don't even notice) unpleasantries of our language of choice, yet be quite averse to the unpleasantries of other languages.

Maybe it's due to sunk cost; time/effort has already been spent finding work arounds for or adapting to the warts of our native tongue, whereas doing so for unfamiliar languages would require additional effort.

No way to prevent "this", says C++ programmer.
Just use Python it uses 'self'
In Python, you can easily just not type "self" (or whatever you named the 1st arg).

In C++, you can not not type "this", at least if you don't, "this" will be looked up anyway.

I would rather have unpleasantries that make the language safer vs unpleasantries that make it more vulnerable. Especially when the unpleasantries in question don’t even make the language easier to use.
C standard: "Undefined behavior means such a situation can't happen."

Me: "If it can't happen then it would be fine to just crash on those situations, right? Because such a crash would never be reached. Can we get that?"

C compilers: "No. Would you want to crash on signed integer overflow, for example?"

Me: "Yes? Would be safer than the current situation at least."

C compilers: "What, no, that would make your programs imperceptibly slower. Would you even like that?"

Me: "Yes, I'll be able to live with that."

C compilers: "Well, the answer is still no."

It would have taken you less time to look up -fwrapv / -ftrapv / -fsanitize=signed-integer-overflow + -fsanitize-undefined-trap-on-error than write out that misleading dialog. :)
Fair.

Last time I checked ubsan was a few years ago for a small program, and I've read stuff about it not being suitable for anything outside development (more like a valgrind complement than a release option) because of how it messed with environment variables and linked libraries or something, so I might not have the full picture.

C is more likely to define wrapping arithmetic. Wrapping is defined as undefined for compatibility with MIPS processors that support checked arithmetic.
There’s probably a large selection bias at work as well – people who care about those specific unpleasantries will avoid that language in the first place.
This is definitely a thing I recognise in myself, a year and a half of writing Scala daily has made me much more prejudiced against Java than I was before.
Alternative headline should be "But I have been taught that using C++ makes me the better programmer" because the stereotypes of echo chambers on the internet raised a lot of unreflected programmers to be this way.

There is a place for C, where there's no alternative. But that place is where 99% of programmers never work, because they are not doing kernel nor firmware development (which, in the meantime, also has a lot of support by and for memory safe VMs and languages).

The issue I have with this narcisstic fatigue (similar to the author's point I assume) is that there is no reflection when they fuck up a codebase.

The best code is the code that is safe and easy to read, and doesn't need to use "clever tricks" that beginners cannot understand.

If you are using some tricks for type casting to implement your ideas into code, you probably should not write code.

Code should be dumb and easily maintainable. If it is not, you made the wrong choice for the programming language.

Kernels have been developed in safer languages already before C became widespread outside Bell Labs, it is a myth that C is even required for that, other than historical baggage.
Especially when one adds how many "low level programming" idioms for C are, as far as I understand, undefined behaviour in C. Like assigning an address to then use as pointer to physical memory...

Which is extra visible when one looks at original UNIX sources and its many short assembly bits in separate files to handle bits of direct hw manipulation.

> Like assigning an address to then use as pointer to physical memory...

What do you mean by this? Like writing to a specific integer address?

    *((volatile unsigned *)(0x20001000)) = 0x12345678;
That's not UB and is also the only way to write to memmapped registers.
It's not UB. It's also not covered by standard - it's implementation-specified. What will happen is dependant on decision of compiler writers.

It is, however, not the only way to write to memmapped registers.

The original C way of doing so was to use an assembly function wrapping the actual act of reading/writing (honestly, better than doing the above, as it helps making it very explicit how the write will happen as well as abstracting any details like needing to add a barrier or whatsoever), the other way was to specify the symbol with address of the memmapped register in assembly, and link resulting object with C code.

A C implementation is, AFAIK, free to refuse the literal addresses used as pointers and pass as ISO C.

Both of the alternative do not generate same code though.

At least they don't without LTO.

> That's not UB

Please cite chapter and verse of the C standard which defines this behavior. Any edition.

I don't know if this particular example is UB or not, but the dichotomy here between 'defined' and 'not defined' is a false one, as C also specifies some constructs as having 'implementation defined' behavior. The behavior of such constructs is not defined in the standard, but is also not 'undefined' in the special sense of 'undefined behavior'.

Edit: Looks like the result in this case is implementation defined: https://stackoverflow.com/a/24212940

> There is a place for C, where there's no alternative.

Alternatives to C have started appearing for a long while, and they are quite mature now.

> because they are not doing kernel development

FluentBit, where this error occurred, is a userspace application.

> there is no reflection when they fuck up a codebase

C does not support runtime reflection, that is correct. It's one of the reasons why it's a programming and debugging nightmare.

> If you are using some tricks for type casting to implement your ideas into code

There seem to be no casting issues involved in https://www.tenable.com/security/research/tra-2024-17.

> The best code is the code that is safe and easy to read, and doesn't need to use "clever tricks" that beginners cannot understand.

It's just a pile of truisms.

Is your whole post even related to this article?

> Is your whole post even related to this article?

I think they were just reiterating TFA message in another way: Don't expose yourself to the pitfalls of C when the actual power of C is not really needed.

> There seem to be no casting issues involved in ...

Maybe go to http://osv.dev/list and search for OOB or Out of Boundary errors instead to make a study on whether this regularly happens or not? Especially when parsing arbitrary strings into a struct?

I'm not sure whether or not you got my message to prefer any programming language over C/C++ or whether you're trying to ignore that on purpose?

>But that place is where 99% of programmers never work, because they are not doing kernel development

Certain embedded targets are also legitimate use cases for C. For example, I am currently working on a project using an 8051 microcontroller. Aside from using assembly – which obviously isn't safe either – there is no practical alternative to using C. Rust may be making inroads for 32-bit targets, but will likely never be able to target an 8051.

> Code should be dumb and easily maintainable.

We now have enough resources to do this. When C was created, we had to do those tricks to have good performance. Currently C is best used for constrained devices, where sometimes you need those tricks.

> If it is not, you, yes ... you, made the wrong choice for the programming language.

Or your project manager or CTO, or some other stakeholder.

> Code should be dumb and easily maintainable.

Well ... that rules out everyone's pet favourite here to replace c.

which is C, because nothing can replace C.

C is portable assembly. Don't use it unless you really must.

Yes I know people want to replace C with Rust, no it won't work because Rust is not assembly, yes it can be used to replace anywhere between 90% to 99% of C and it shouldn't even be too hard. (Unless your embedded device manufacturer doesn't care, which of course they don't.)

> C is portable assembly.

Unfortunately, it isn't quite this, and many of the instances which people use it for this are actually undefined behavior; there's a hinterland of "things C compilers do" which are used as portable asm but are not part of C because they are UB.

I sometimes wonder if it would be feasible to define an actually portable typesafe macro assembler.

Only if you mean K&R C, where C was mostly a portable macro assembler, calling into Assembly code, written and compiled via an external assembler like in the first iteractions of the UNIX rewrite from Assembly into C.

The Lions book is a good example of what that C used to be like.

> The issue I have with this narcisstic fatigue (similar to the author's point I assume) is that there is no reflection when they fuck up a codebase.

Forgive my bluntness, but do you even know any C programmers? The ones I know are not at all a homogenous group (there isn't even anything resembling a "C community" - and IMHO that's a good thing btw).

> The best code is the code that is safe and easy to read.

These two things are not really related.

- Rust code is safe, but typically not easy to read

- C code is unsafe, but typically easy to read

...as a counter example though, C++ code is unsafe, and typically not easy to read.

Of course "readability" is entirely subjective too. Seasoned programmers are typically blind to the problems of their language of choice (and that includes C's sloppiness, and Rust's readability). Language beginners typically stumble over problems that experienced users of that language wouldn't even think of.

> Code should be dumb and easily maintainable.

Well, obviously, but see above, one programmers 'dumb and maintainable' is another programmers 'unmaintainable complicated mess'. IME it's harder to write code that's 'dumb and maintenable' in languages that nudge the programmers towards sugarcoating the program logic in high level abstractions than in "simple" languages like C, Go or Zig, because you don't just need to know the language, but also understand the high level abstractions the original author came up with (and which might have been a good fit at the start of the project, but not 3 years later when requirements have changed and the original programmers have long left for greener pastures).

IME it's not 'narcissism' that makes programmers pick C, but the incredible flexibility of the language, combined with being quite predictable what code will be generated (yes, optimizer passes do complex things, but after a while you'll develop an intuition for what the compiler will do, verifiable via godbolt).

Also for instance, look at the hoops other languages are willing to jump through just to get close - but never fully match - what's possible with simple text replacement in the C preprocessor (an incredible hot take, I know).

> C code is unsafe, but typically easy to read

Are you joking?

Absolutely not, modern C code (eg C99 or later) looks a lot different than C89. One problem is that the C stdlib is stuck in the C89 era, but at the same time most of the stdlib isn't all that important for writing C code).
Some of the HN discussion about whether "new projects in C should be allowed" is moot: Fluent Bit was imported into git in 2015 [0] (a few months before Rust's first public release), and may be considerably older than that for all I know.

I suppose incidents like this actually do give a reason to "rewrite it in Rust", when "it" is "widely deployed infrastructure written in C". OTOH, I'm sure there were plenty of non-memory-safety bugs introduced and later fixed over the years, and rewriting in Rust will recapitulate that subset of bugs.

[0] https://github.com/fluent/fluent-bit/commit/49269c5ec3c74411...

People slipping backdoors into stuff are no doubt super enthusiastic about Rust both for the opportunity for new anonymous nobodies to rewrite long stable and proven tools as well as the dependency ecosystem that tends to blindly pull in multiple different entire HTTPS/TLS stacks into anything but the most trivial software.
I don't buy it. Rust has a really good track record on attracting more people to read and modify the code, which isn't what you want if you're hiding backdoors in the code.

In decades of writing C (sometimes as a hobby, often for a lot of money) I'd guess I thought "These errors when I wrote bugs in my program are crap, somebody should fix it" maybe once per month on average. But a C compiler is very intimidating code, subtle and hard to even build from scratch let alone contribute to, so I never attempted to make such changes.

In only a few years of writing Rust (none of that paid) exactly twice I've thought "Man this compiler error diagnostic isn't very good, somebody should fix it". The first time I asked on Reddit, and I was informed that I wasn't the first to notice, the fixed diagnostic was in nightly Rust already. The second time I found the diagnostic and I just fixed it, compiled first time, wrote a new unit test, checked that passed, wrote a pull request. Landed it. Then I wrote a HN comment, a reader found a bug in my diagnostic, so I fixed the original code, and wrote a new PR which also landed. If Rust has told you that instead of 'X' when you needed a byte, you should write b'X' because just 'X' is a char not a byte - that's me, that's my small fix. [Before the fix 'X' wasn't legal here, of course, but the diagnostic wouldn't suggest what to write instead]

I'm not sure how modifying the compiler is relevant to the point. I think it's the general trend that early in languages lives its much easier to contribute to the tools, after decades of amassing improvements (such as yours!) they tend to become less accessible.

But regardless, the "rewrite in rust" advocacy has created a significant opportunity for projects created by single people, without outside review and often without significant domain expertise (at least where they are slavish re-implementations of existing code), to be proposed as replacements for longstanding stable tools. Whatever the merits of that chance it's also dream for someone looking to introduce new vulnerabilities. Even where the replacement itself is reviewed it will usually come with a massively expanded dependency footprint which isn't.

The compiler is illustrative. A large piece of Rust software, nevertheless easier to read and contribute to when compared to similar software in the same category.
Um, no.
`this` was mostly prevented in JS with the introduction of arrow function expressions
I had someone on Reddit r/webdev try to convince me that 'this' was not a mistake but a powerful language feature. A small glimpse into the mind of JS fans.
Sounds like something any programmer would say about their preferred language. Can't say you don't hear similarly crazy things about C, or C++, or Java.
True. But it's generally only common with older C-based languages where the goal was packing as many features as you can instead of high level design aimed to guard software from inexperienced overeager devs.
It kinda is a powerful language feature But with great power comes great responsibility, and that doesn’t mean it was a good idea in hindsight.
Yes, it's functions vs methods. You can make this explicit like in Python, you can make it implicit but have two kinds of methods, instance methods and static methods like in C++/C#/Java.

And you have JavaScript were all functions have an implicit this ...

If it didn't exist, people would come up with weird hacks to implement the few cases where you do need it. But those are rare.
Oh no ... a bug in a C program. This easy bashing on existing C programs is getting boring and annoying.

Write a new userspace program? Use anything else, all language shave flaws so pick one which supports the features you need. Want to quickly write something because you are not allowed to rewrite the entire ecosystem you need into a new language since the project will go massively over budged: Use what you can and what's available and accept the risks, which is the 99th percentile of software.

Free Pascal and Lazarus which is a GUI built on it support strings that don't require manual allocation and are counted and reference counted. A huge amount of grief would go away if that library could be supported in the Linux kernel somehow, and all of the string parameters in system calls ported.
> a vulnerability in HTTP parsing code that allows for heap corruption and arbitrary code execution by making a HTTP GET request with a megabyte of the letter 'A' in its body

You mean a buffer overflow? Why write so technical then dumb down something that's pretty obvious.

buffers are stored on heap.
Thoughts and prayers
I bet this person felt really smart posting about this problem that surely nobody has ever thought of before
A) "surely nobody has ever thought of [this] before]" says person who hasn't read https://xeiaso.net/shitposts/no-way-to-prevent-this/

B) It's a spin on The Onion headline about school shootings.

A) I know, it still sounds smug and condescending

B) I know, it still sounds smug and condescending

Terrible analogy. School shootings are not the result of mistakes. They are intentional acts.
Prince Marcel O'Keefe must be C royalty!
Needs more furries.
Yeah, was about to note that the link is unexpectedly SFW.
Why? Furries are NSFW because they look cartoonish and "unprofessional"? I don't like them but what a boring workplace would that be.
They're being satirical. People usually berate my posts because I have cartoon characters for Socratic exchanges to teach people things like Kubernetes, claiming that is "unprofessional" or something. These people are sarcastically berating my post for NOT using that Socratic system.
IMO GGP wasn't being sarcastic, but GP maybe was. Anyway I keep my thought: if a workplace, especially if in Tech or Tech-adjacent, sees furries as NSFW, it's a very boring workplace and I would run away from it at the first possibility.
No No No, I genuinely love both your Socratic exchanges and the furries characters that illustrate them. (although this is article is firmly Aristophanes, again, top notch work)

I don't hold with the "I'm not going to open a link on HN from Xe at work" bollocks, because I know its going to be a gem, moreover, I'm not a fucking prude.

If one can, at work, open the daily mail, "listen" to music videos, or read celeb gossip sites at work, then you can 100% look at computer using anthropomorphic animals.

Plus ever since I read your article about abusing S3 into a message queue, I've started using 18th century engravings of gargoyles, statues and classic sculpture to aid my presentations.

In summary, I love you work, I wish you to continue making it, and please don't take my comment as sarcastic. As I said before many months ago, this is what the internet was designed for, long may it continue.

It's gross because I don't want to think about your sex life when at work, but I guess this type of exhibitionism is fine.
Why did you jump from 'cartoon characters having Socratic conversations' to 'sex life'? Good grief.
No I'm not. I expected that there is some furry or anime girl visible when i click on that link. This is the stuff i do not want to have on my screen when there are coworkers nearby.
The headline is misleadingly focusing on a soundbite out of the full quote.

"It's a shame, but what can we do? There really isn't anything we can do to prevent memory safety vulnerabilities from happening if the programmer doesn't want to write their code in a robust manner." -- Some (uncredited?) C programmer.

Does C have more footguns as a low level language? Of course. That's part of the freedom of bringing only the baggage a project needs. Sadly, like many dangerous or sharp tools, incorrect use will lead to harms.

If someone has a choice, a safer more modern language can accommodate less skilled practitioners.

It's a deliberate echo of the famous Onion headline about America's absolutely disgraceful pretence that it couldn't do anything about all the shootings.

https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...

> If someone has a choice, a safer more modern language can accommodate less skilled practitioners.

This is the usual mistake. It's not a "skill issue". Footguns are a design mistake, they're much more dangerous than necessary hence the name. As a result the practitioners accomodated by a better language are all those capable of mistake ie all humans.

Rather like the school shooting issue, the C issue is bound up with people at the identity level; they insist on the danger because they cannot stand the possibility of the danger being taken away from them. Their ability to use guns or C safely must not be questioned. They percieve it as an insult to take that choice away from them. They are the safe C programmer that never ships a CVE. They know that they themselves would never shoot up a school, so what the problem? Oh it's bad people. Well, that's happening somewhere else to other people, so it's fine and they can carry on.

(edit: someone was helpful to provide an example in this thread; https://news.ycombinator.com/item?id=40438716 )

Here's an attempt to explaining the irrational part of programmers deciding which programming language to use. Part of it goes into more detail on ideas like C programmers insisting 'on the danger because they cannot stand the possibility of the danger being taken away from them'.

"The Pervert's Guide to Computer Programming Languages" https://www.youtube.com/watch?v=mZyvIHYn2zk

It's pretty out there, but I thought also interesting.

Congratulations, you just landed in my very small number of favorite comments.
One can in fact not do anything about something for which one refuse to even consider the root causes of.

In the case of shootings, one must talk about what may have caused shootings to start noticeably increasing in around the 1970s, to the point where a single year started having as many shootings as all the shootings prior to the 20th century combined.

Fun fact; the first documented school shooting was perpetrated by some Lenape indigenous people, in Pennsylvania in 1764, during which attack they also beat to death all of the students and scalped everyone.

> Fun fact; the first documented school shooting was perpetrated by some Lenape indigenous people, in Pennsylvania in 1764, during which attack they also beat to death all of the students and scalped everyone

That would probably count under modern definitions of "terrorism". I would also say that most school shootings should also be counted as "terrorism", except there's a very strong ideological push to not look at them this way. Don't look at the radicalisation. Don't read the (suspiciously similar) manifestoes. Don't look at who they cite for inspiration.

If you're going to go back there far, before it was independent, I bet there was something documented in medieval Oxford or Cambridge involving a longbow and/or an anelas.
[Also writing "skill issue" caused me to immediately think of Gill Issue, the Grand Poo World 3 course with a name that's a pun on skill issue, so for anybody else whose brain works the same way, here's somebody competent beating Gill Issue: https://www.youtube.com/shorts/I6PdLgUGHaw ]
>If someone has a choice, a safer more modern language can accommodate less skilled practitioners

The implication here is thoroughly debunked. We’ve seen over and over again that memory safety bugs will happen in every C codebase(ignoring toy or small codebases). Even for the mythical and infallible “skilled practitioner” spending time re-solving the solved issues inherent to C just isn’t a good use of developer time.

> a safer more modern language can accommodate less skilled practitioners.

That’s really what it’s all about. SV is absolutely obsessed with hiring bad programmers, treating them like crap, so they don’t stick around, and somehow, magically, forcing them to write good code.

We have this belief that if we just use the “right” tool (in this case, a particular programming language), all of our products will be good. Couple that, with the belief that we should be able to build our stuff on someone else’s code, for free, and you have a recipe for disaster.

People like Linus Torvalds are living proof that it is quite possible to write amazing stuff, in old languages (he is a C guy), but people like that, are rare as hen’s teeth, and may be difficult for today’s tech managers to handle.

There really is no substitute for running a good shop, hiring good people, training them well, treating them well, paying them well, and keeping them around for significant lengths of time.

Also, we need to be able to hold ourselves accountable for the Quality of our own work -regardless of the tools we use. Torvalds is notorious for being a tough taskmaster, because he’s serious about the Quality of his work, and casts that onto others.

“Treating people well” does not mean using kid gloves. It can mean expecting people to act like grown-ups, produce grown-up work, and not accepting less. I worked in an environment like that for decades. It was often quite stressful, but was also personally rewarding.

It isn’t the tools that are broken; it’s the management culture, and no one wants to fix that.

I have a longer nuanced post to write out at some point about my feelings here, but here's the cliff's notes:

We should not be developing new projects in C, and we should make it politically hard to make new projects written in C in distribution repositories. C is going to stay around for a long time and that's okay, but there are so many inherent problems of its design and implementation that make it unsuited for unsupervised use. I wish that we didn't have to make such hard-line stances about this, but people have continuously proven that this tool is unsuitable for modern development and it's only going to get worse as time goes on.

> We should not be developing new projects in C

I would give at least embedded development a pass, since you rarely do have a choice there

It's difficult, because "embedded not networked" is an environment where security risks are low, but "embedded networked" is a really nasty environment of haunted routers and abandoned IoT devices.

Is it acceptable to risk buffer overruns on the HTTP server running on an insulin pump?

Mostly because you often get shipped half-broken pile of C-based SDK as your blessed environment.

Not because there's no other options (and long before Rust had its name coined)

Well, yes. I'm more focusing on user space applications here. This is why that nuance post would have to be so long. People here would probably misconstrue it to be that I want to "kill C" though. That's part of the reason why I haven't written it yet lol
I agree. I regularly use Rust on embedded devices and what many advocates like to leave out is that you're just using regular old C under the hood anyway. It's bindings all the way down.
If your distribution refuses to package useful free software, I'll use another distribution. I think this is true for most people.
The story is satire. But the satire is illustrating true problems and attitudes.
A safer language accommodates every programmer.

Nobody writes flawless code.

What’s missing from Rust? I don’t care if you use C, but don’t pretend like there isn’t another option
Simplicity.
With all the language features I agree.

However, if you reduce the language surface it is possible to have something safe and simple enough (IMHO).

For instance, you can say no async, no custom traits and only {Debug, Display, Eq, PartialEq, ...} are allowed for your structs and generics. From limited personal experience that takes away more than half of the complexity of navigating rust code.

The more you take away, the closer you are to a simple but unsafe language. If you remove the "unsafe" keyword, many things you can't solve easily nor optimally.

You might be able to outsource some complexity to external libraries, but integrating libraries is itself a major headache, and it can lead to security issues too.

Fair enough. But unsafe for kernel code I guess it's a necessary evil (that's why I didn't mention it).

However, I believe the being "opt-in" by explicitly marking sections unsafe is the way to go instead of having unsafe by default (which is the only way using C).

Unsafe is a daunting language feature, but ultimately it’s a feature. You’re meant to use it if you need it.

You don’t need to outsource complexity anywhere. Rust is fully capable.

This. Learning Rust is like a long series of jousting matches with the compiler.
It's more than that. Even after you learn it, you will struggle a lot with productivity, especially fast iteration and incremental changes and fixes, as Rust forces you to redesign and refactor your code.
How is that different than learning any other language?

I think Rust’s reputation for being hard to learn is overblown. Yes it is probably a little more esoteric than what you’re familiar with but any decent dev should be able to pick it up

The alternative is jousting with attackers. In production. When you're not aware of it. You're not even on the field, let alone mounted on your horse and wearing armour.
Simple | Fast | Safe. Choose two.
Sacrificing safety might have been defensible on a 40 MHz 80386 that could barely run a C++ compiler (I think this is what I started my career on) but today we find eight-core 3.8 GHz CPUs in desktops and dual-core 1.8 GHz SiPs in wristwatches. Safety should have become table stakes a while ago; Moore's Law already paid for it.
Exactly this. It's all about trade-offs. You need to pick the right tool for the job. Sometimes the right tool is dangerous. Knives are dangerous tools, but you need them if you want to cook a meal.
I love one comparison to hole hawg[0] (used for unix vs windows&mac), which is usable here. Memory safe languages are like a fancy electric drill from target. They get the job done, but if you want some more serious drilling they die on you instead of doing something dangerous. C is like one of those drills which are comprised of engine and a handle (cheap, changeable piece of steel pipe). They are designed to do one thing: they rotate a drill. When drill blocks, they rotate you. But when you take appropriate precautions, boy do it drills...

This is from my favourite book: In the beginning was the command line.

[0] https://web.stanford.edu/class/cs81n/command.txt , search "HOLE HAWG".

People overwhelmingly chose Simple and Fast. Still.
This isn't a crazy thought to have about K&R C. They're trying to fit a high level language onto a 1970s computer and so sacrifices must be made. Some of the trades they make are... questionable and others I'd say clearly wrong (they just don't need Tony's billion dollar mistake, nor to be so cavalier with types in general), but it's not as though they're targeting a machine with gigabytes of RAM and a multi-core CPU.

But, people aren't writing K&R C. These days most of them are writing something closer to C99 or C11 and some may be using something newer (e.g. C17) or proprietary (GNU's C dialect) either deliberately or just because it compiled and nobody told them not to.

At that point you've actually given away much of the simplicity, and yet what you get for your trouble is largely more footguns. Trade up for a sound type system and fewer footguns instead.

ISO C comes with a list of constructs that the language is entitled to silently miscompile. Varying from "those bytes can't be an integer, I'll delete that function for you" through "signed integers can't overflow, so that overflow check you wrote must return false, so I'll delete it for you".

This makes simple application code slightly faster. That's kind of a reasonable domain for C++ but on dubious ground for C. Where C is superb is talking to hardware and language implementation, exactly the areas the ISO group has chosen to cripple the language for.

Thankfully compilers know about this and provide fno-strict-aliasing et al. Maybe there should be a std= style flag to change to the language subset that doesn't actively try to miscompile your program chasing benchmark numbers.

What things simplicity do you lose? I think there isn't much of a difference between these dialects. Most important one might be possibility to define variables on the spot instead of at top of block. Then, some people like C99 compound literals. I don't think any of these break simplicity, they are quality-of-life improvements with no interactions with the rest of the language semantics.

Next one is what, the C11 memory model? Doesn't take away any simplicity, just defines things better.

Here's my thinking. It's fair to say C99 isn't that much more complicated than C89, which formalizes various things that are a bad idea such as "volatile", as well as numerous good ideas like hey we should let you define a variable where you use the variable - however C99 adds more of the bad like "restrict".

In both those cases the K&R C model was very simple. You could decide you love how simple this model is, and when smarter compilers optimise it into a pretzel or other languages are faster that's OK. This code used to drive the serial port, now it does nothing, OK, don't use C to write such drivers. This code used to go real fast, now everybody else is faster, OK, don't use C if you need the best performance.

C89 and C99 choose different, making the language more complicated to keep addressing performance and compatibility. In C99 I can write the fast serial port driver, but it's significantly more complicated as a result. The beginner will definitely get it wrong and explaining why is pretty subtle.

Then C11 says actually you're not writing sequential programs, which was a crucial simplification in K&R C - the programs you can write do one thing at a time, in order, and then maybe stop. The memory model in C11 is needed because it says actually your programs might do more or different things at once.

Now, in reality by 2011 lots of people were writing C that's not actually sequential - after all SMP Linux long pre-dates C11. But those weren't legal C programs, they're GNU's dialect and so all bets are off. Nobody is claiming Linux is simple.

So C11 definitely isn't the simple language for a 1970s computer any more. C11 is a competitor with C++ or today Rust. And it doesn't fare so well by that comparison.

K&R gets used as a shorthand for the "portable assembly" C that some people remember and others swear never existed. That's my guess at what the parent meant, not the syntactic strangeness of writing types after the parameter list.

C11 atomics would have been much better if they'd standardised existing practice instead of inventing something based around the application centric design C++ was inventing. It's nice that there's an acquire/release scheme one can reason with but a real shame that they conflated it with type annotations.

Well computing has gotten a lot more complex since c’s inception. It’s model has held up well but the cracks are there
Programs are a lot more complex. The computers aren't.

They were far less homogenous in the early years. Today you have a octet addressed little endian integer machine, with ieee float hardware and a small vector unit. Maybe you have two different instances of that on the same address space, but probably just one.

I think reasonable argument could be made that the complexity in modern computing is primarily self inflicted by software engineers.

Computers have gotten much more complex. Maybe your OS provides that nice little bubble for you, but it’s an illusion
If the borrow checker was optional and not baked into the language. If it compiled fast and not slower than c++.

I might try it out if these two requirements were met. I have a mental model between what I write in C and what I can expect the assembly to be.

Or maybe the problem is that we have the same stack for code and data? could that be it?.

My problem with Rust is that it’s a hammer, and every nail is memory safety. If I’m writing a TLS library or kernel module, yes, memory safety is paramount. But if I’m writing pretty much anything else, Rust isn’t worth the slog.
How is that a problem with rust? Like any language it's a tool with strengths and weaknesses. Don't use it when it doesn't make sense
Sorry, should have been clearer. I was thinking in the context of every online discussion I’ve run across around “Rust vs. any other language,” where the diehard Rustaceans insist that we should just rewrite everything in Rust.

Totally agree with you: use the best tool for the job.

> only the baggage a project needs

What projects need manual memory management? Those where the hardware costs are comparable to development/maintenance costs. That is much rarer than people think.

RAM is cheap, and few applications really need bespoke allocation. And it's not just a question of skill; even the most disciplined make mistakes. It's one of how much brainpower you want to allocate to... memory allocation.

>What projects need manual memory management

Games.

Big/specialiased games to be precise, as for smaller projects managed language offer good enough performance.

In practice I found C/C++ "manual memory management" fans to know surprisingly little about memory management, or even how much manual memory management costs them in performance.

High end games programming sometimes knocks the love of malloc()/free() (and naive RAII) out of them.

In practice I find malloc()/free() mentioned almost exclusively by GC proponents. I don't know any "manual memory management" fans but only people who think that GC is worse than fully designing a memory management scheme for data-intensive applications. And these people shy away from using malloc(), because it is 1) slow/unpredictable (and yes, slower than GC allocation) 2) A general purpose allocator, which gives us little control 3) requires to match every call with a corresponding free(), which is tedious and error prone.

Note also that with (tracing) GC, you not only pay for the cost of allocation, but also pay for the time your allocated objects live.

Then you have a pretty nice bubble (this is not in jest, I honestly envy you).

A more nuanced take is that I can, in practice, map considerable chunk of developers I met and their understanding of memory as:

"it's magic" > "manual memory management rulz! my C/C++/Rust will be always better than GC" >>> people who actually studied automatic memory management a bit >> people who adjust memory management techniques to specific use cases

The fall off is pretty much exponential in amount of people, which I will admit does make some people defensive thanks to second group being both numerically big and undying because programmers suck at educating new generations. Same can be seen in various other areas, not just GC (though GC links with general trope of "opinions formed from people repeating things learnt from xeroxed half dog eaten copies of notes somewhat relevant on 256kB IBM 5150 PC and Turbo Pascal")

A lot of the time I do encounter people who essentially parrot claims about GC without any understanding of what's happening underneath, and indeed sometimes going about "crazy awesome non-GC solution!" that turns out to call out to whatever malloc()/free() happens to live in libc when I pull the cover and with just as much unpredictability as you can get.

As for paying - allocations in GC systems tend to be blindingly fast, but yes, collection does involve other costs.

What does knowing a lot about memory management look like? Is it like being familiar with heap allocation, the structure of fastbins and co., and some knowledge of lifetime and ownership?
In addition to that, also knowing at least basics (a bit like "classes of algorithms") of automated memory management, memory pooling, understanding and using the knowledge of allocation and mutation patterns in one's program.

A funny story related to all that from one of my previous jobs includes a high performance C++ code base that used forking to utilize preloaded code and data and to easily clean up after itself.

Turned out that naive-ish (even with jemalloc dropped in) memory management in C++ code resulted in latency spikes not because of allocation/freeing, but because they put no control over where things got allocated which resulted in huge TLB and pagetable thrashing as copy-on-write got engaged after forking.

To the point that using something like Ravenbrook's Memory Pool System (a GC) with threads quite possible would let them hit performance targets better.

The lack of memory safety is a property of C implementations, not the language itself. You can have an implementation that reliably crashes on memory errors if you want, though it does get easier if you leave out a few features like casting pointers to ints and back.
There is a multiple decade long history of coaxing a memory safe C embodiment. If it was possible to simply implement memory safety without fundamentally changing the language, you would have a compiler flag that would give to you, requiring no source modifications.

No compiler gives you that memory safety flag (aside from very specific security features, ex. -fstack-protector) because it's not possible in C.

It is totally possible for pointers with bounds checking to exist in C. Turbo C did that 30 years ago. However, why bother trying when whole culture is biased against it and you can use any of the other languages?
>Turbo C did that 30 years ago.

I used Turbo C++ (not C) back in the day, and recall near pointers, far pointers, based pointers, but I don't recall bounds-checked pointers. Do you remember any details?

>However, why bother trying

Because it would immediately make the large amounts of C code still in the wild safer for the users, who do care?

I remember it as an compiler otion.
For the sake of the example let's assume you make a C to Python compiler/transpiler which uses native Python arrays for every stack or heap based allocation and every pointer arithmetic would be replaced by a checked operation, you'd quite likely get a memory safe language in the 'shellcode impossible unless you really set the program up for it' sense.
https://discourse.llvm.org/t/rfc-enforcing-bounds-safety-in-...

You can also run with ASan on, or compile into WebAssembly and run that… obviously not perfect though.

This "accommodate less skilled practitioners" you wrote is something that never fails to puzzle me from C/C++ practicing colleagues: the idea that in the problem space there is a high-risk high-reward zone of top performance where only the heroes dare tread.

I am sorry but my observarion is that Nim and LLVM and dare I say even GC-less Java all achieve the same performance as C without the footgun baggage. Sometimes it is good to find a way to move on.

>I am sorry but my observarion is that Nim and LLVM and dare I say even GC-less Java all achieve the same performance as C

This is just false. Sure, they probably are 'close enough', but you can't claim the performance is 'the same' simply because those languages inherently do more to achieve the same results.

C inherently does spend more CPU time on memory management than some GC systems.

The fact that a lot of C/C++ fans do not properly profile those areas of their programs does not mean they do not pay for them.

Or that defensive coding (assuming they do not want to introduce a happy little accident of CVE into the world) is free compared to having compiler lift necessary asserts outside of hot loop.

Some memory-safe languages even provide a faster feedback loop on exact instructions that will be executed for all code you write (probably most famous example is Common Lisp, which mandates that DISASSEMBLE is accessible to developer at all times)

I'm trying to decide if this is a good satirical response to a satirical article? If so, well done!

Because at this point, where can we find programmers who aren't 'less skilled practitioners'? Because it's clear Microsoft, Google, Apple, and major open source projects, have failed to find them based on memory corruption issues in the Linux, Windows and Mac kernels, chrome, and firefox.