[HN Gopher] Love C, hate C: Web framework memory problems
       ___________________________________________________________________
        
       Love C, hate C: Web framework memory problems
        
       Author : OneLessThing
       Score  : 75 points
       Date   : 2025-10-10 03:39 UTC (19 hours ago)
        
 (HTM) web link (alew.is)
 (TXT) w3m dump (alew.is)
        
       | jacquesm wrote:
       | There are many, many more such issues with that code. The person
       | that posted it is new to C and had an AI help them to write the
       | code. That's a recipe for disaster, it means the OP does not
       | actually understand what they wrote. It looks nice but it is full
       | of footguns and even though it is a useful learning exercise it
       | also is a great example of why it is better run battle tested
       | frame works than to inexpertly roll your own.
       | 
       | As a learning exercise it is useful, but it should never see
       | production use. What is interesting is that the apparent
       | cleanliness of the code (it reads very well) is obscuring the
       | fact that the quality is actually quite low.
       | 
       | If anything I think the conclusion should be that AI+novice does
       | not create anything that is useable without expert review and
       | that that probably adds up to a net negative other than that the
       | novice will (hopefully) learn something. It would be great if
       | someone could put in the time to do a full review of the code, I
       | have just read through it casually and already picked up a couple
       | of problems, I'm pretty sure that if you did a thorough job of it
       | there would be many more.
        
         | drnick1 wrote:
         | > What is interesting is that the apparent cleanliness of the
         | code (it reads very well) is obscuring the fact that the
         | quality is actually quite low.
         | 
         | I think this is a general feature and one of the greatest
         | advantages of C. It's simple, and it reads well. Modern C++ and
         | Rust are just horrible to look at.
        
           | messe wrote:
           | I slightly unironically believe that one of the biggest
           | hindrances to rust's growth is that it adopted the :: syntax
           | from C++ rather than just using a single . for namespacing.
        
             | jacquesm wrote:
             | I believe that the fanatics in the rust community were the
             | biggest factor. They turned me off what eventually became a
             | decent language. There are some language particulars that
             | were strange choices, but I get that if you want to start
             | over you will try to get it all right this time around. But
             | where the Go authors tried to make the step easy and kept
             | their ego out of it, it feels as if the rust people aimed
             | at creating a new temple rather than to just make a new
             | tool. This created a massive chicken-and-the-egg problem
             | that did not help adoption at all. Oh, and toolchain speed.
             | For non-trivial projects for the longest time the rust
             | toolchain was terribly slow.
             | 
             | I don't remember any other language's proponents actively
             | attacking the users of other programming language.
        
               | imtringued wrote:
               | Software vulnerabilities are an implicit form of
               | harassment.
        
               | messe wrote:
               | I'm hoping that's meant to satirise the rust community,
               | because it's horseshit like this that makes a sizeable
               | subset of rust evangelists unbearable.
        
               | 01HNNWZ0MV43FF wrote:
               | > I don't remember any other language's proponents
               | actively attacking the users of other programming
               | language.
               | 
               | I just saw someone on Hacker News saying that Rust was a
               | bad language because of its users
        
               | jacquesm wrote:
               | Yawn. Really, if you have nothing to say don't do it
               | here.
        
               | lelanthran wrote:
               | > I believe that the fanatics in the rust community were
               | the biggest factor.
               | 
               | I second this; for a few years it was impossible to have
               | any sort of discussion on various programming places when
               | the topic was C: the conversation would get quickly
               | derailed with accusations of "dinosaur", etc.
               | 
               | Things have gone quiet recently (last three years,
               | though) and there have been much fewer derailments.
        
           | citbl wrote:
           | The safer the C code, the more horrible it starts looking
           | though... e.g.                   my_func(char msg[static 1])
        
             | uecker wrote:
             | Compared to other languages, this is still nice.
        
               | jacquesm wrote:
               | It is - like everything else - nice because you, me and
               | lots of others are used to it. But I remember starting
               | out with C and thinking 'holy crap, this is ugly'. After
               | 40+ years looking at a particular language it no longer
               | looks ugly simply because of familiarity. But to a
               | newcomer C would still look quite strange and
               | intimidating.
               | 
               | And this goes for almost all programming languages. Each
               | and every one of them has warts and issues with syntax
               | and expressiveness. That holds true even for the most
               | advanced languages in the field, Haskell, Erlang, Lisp
               | and more so for languages that were originally designed
               | for 'readability'. Programming is by its very nature more
               | akin to solving a puzzle than to describing something.
               | The puzzle is to how to get the machine to do something,
               | to do it correctly, to do it safely and to do it
               | efficiently, and all of those while satisfying the
               | constraint of how much time you are prepared (or allowed)
               | to spend on it. Picking the 'right' language will always
               | be a compromise on some of these, there is no programming
               | language that is perfect (or even just 'the best' or
               | 'suitable') for all tasks, and there are no programming
               | languages that are better than any other for any subset
               | of all tasks until 'tasks' is a very low number.
        
             | moefh wrote:
             | I don't understand why people think this is safer, it's the
             | complete opposite.
             | 
             | With that `char msg[static 1]` you're telling the compiler
             | that `msg` can't possibly be NULL, which means it will
             | optimize away any NULL check you put in the function. But
             | it will still happily call it with a pointer that could be
             | NULL, with no warnings whatsoever.
             | 
             | The end result is that with an "unsafe" `char *msg`, you
             | can at least handle the case of `msg` being NULL. With the
             | "safe" `char msg[static 1]` there's nothing you can do --
             | if you receive NULL, you're screwed, no way of guarding
             | against it.
             | 
             | For a demonstration, see[1]. Both gcc and clang are passed
             | `-Wall -Wextra`. Note that the NULL check is removed in the
             | "safe" version (check the assembly). See also the gcc
             | warning about the "useless" NULL check ("'nonnull' argument
             | 'p' compared to NULL"), and worse, the lack of warnings in
             | clang. And finally, note that neither gcc or clang warn
             | about the call to the "safe" function with a pointer that
             | could be NULL.
             | 
             | [1] https://godbolt.org/z/qz6cYPY73
        
               | lelanthran wrote:
               | > I don't understand why people think this is safer, it's
               | the complete opposite.
               | 
               | Yup, and I don't even need to check your godbolt link -
               | I've had this happen to me once. It's the implicit
               | casting that makes it a problem. You cannot even typedef
               | it away as a new type (the casting still happens).
               | 
               | The real solution is to create and use opaque types. In
               | this case, wrapping the `char[1]` in a struct would
               | almost certainly generate compilation errors if any
               | caller passed the wrong thing in the `char[1]` field.
        
         | OneLessThing wrote:
         | I agree that it reads really well which is why I was also
         | surprised the quality is not high when I looked deeper. The
         | author claims to have only used AI for the json code, so your
         | conclusion may be off, it could just be a novice doing novice
         | things.
         | 
         | I suppose I was just surprised to find this code promoted in my
         | feed when it's not up to snuff. And I'm not hating, I do in
         | fact love the project idea.
        
         | lifthrasiir wrote:
         | Yeah, I recently wrote a moderate amount of C code [1] entirely
         | with Gemini and while it was much better than what I initially
         | expected I needed a constant steering to avoid inefficient or
         | less safe code. It needed an extensive fuzzing to get the
         | minimal amount of confidence, which caught at least two serious
         | problems---seriously, it's much better than most C programmers,
         | but still.
         | 
         | [1] https://github.com/lifthrasiir/wah/blob/main/wah.h
        
           | jacquesm wrote:
           | I've been doing this the better part of a lifetime and I
           | still need to be careful so don't feel bad about it. Just
           | like rust has an 'unsafe' keyword I realize _all_ of my code
           | is potentially unsafe. Guarding against UB, use-after-free,
           | array overruns and so on is a lot of extra work and you only
           | need to slip up once to have a bug, and if you 're unlucky
           | something exploitable. You get better at this over the years.
           | But if I know something needs to be bullet proof the C
           | compiler would not be my first tool of choice.
           | 
           | One good defense is to reduce your scope continuously. The
           | smaller you make your scope the smaller the chances of
           | something escaping your attention. Stay away from globals and
           | global data structures. Make it impossible to inspect the
           | contents of a box without going through a well defined
           | interface. Use assertions liberally. Avoid fault propagation,
           | abort immediately when something is out of the expected
           | range.
        
             | uecker wrote:
             | I strategy that helps me is just not use open-coded pointer
             | arithmetic or string manipulation but encapsulate those
             | behind safe bounds-checked interfaces. Then essentially
             | only life-time issues remain and for those I usually do
             | have a simple policy and clearly document any exception. I
             | also use signed integers and the sanitizer in trapping
             | mode, which turns any such issue I may have missed into a
             | run-time trap.
        
               | OneLessThing wrote:
               | This is why I love C. You can build these guard rails at
               | exactly the right level for you. You can build them all
               | the way up to CPython and do garbage collection and
               | constant bounds checking. Or keep them at just raw
               | pointer math. And everywhere in between. I like your
               | approach. The downside being that there are probably
               | 100,000+ bespoke implementations of similar guard rails
               | where python users for example all get them for free.
        
               | jacquesm wrote:
               | It definitely is a lot of freedom.
               | 
               | But the lack of a good string library is by itself
               | responsible for a very large number of production issues,
               | as is the lack of foresight regarding de-referencing
               | pointers that are no longer valid. Lack of guardrails
               | seems to translate in 'do what you want' not necessarily
               | 'build guard rails at the right level for you', most
               | projects simply don't bother with guardrails at all.
               | 
               | Rust tries to address a lot of these issues, but it does
               | so by tossing out a lot of the good stuff as well and
               | introducing a whole pile of new issues and concepts that
               | I'm not sure are an improvement over what was there
               | before. This creates a take-it-or-leave it situation, and
               | a barrier to entry. I would have loved to see that guard
               | rails concept extended to the tooling in the form of
               | compile time flags resulting in either compile time
               | flagging of risky practices (there is some of this now,
               | but I still think it is too little) and runtime errors
               | for clear violations.
               | 
               | The temptation to 'start over' is always there, I think C
               | with all of its warts and shortcomings is not the best
               | language for a new programmer to start with if they want
               | to do low level work. At the same time, I would - still,
               | maybe that will change - hesitate to advocate for rust,
               | it is a massive learning curve compared to the kind of
               | appeal that C has for a novice. I'd probably recommend Go
               | or Java over both C and rust if you're into imperative
               | code and want to do low level work. For functional
               | programming I'd recommend Erlang (if only because of the
               | very long term view of the people that build it) or
               | Clojure, though the latter seems to be on its retour.
        
           | OneLessThing wrote:
           | This is exactly my problem with LLM C code, lack of
           | confidence. On the other hand, when my projects get big
           | enough to the point where I cannot keep the code base
           | generally loaded into my brains cache they eventually get to
           | the point where my confidence comes from extensive testing
           | regardless. So maybe it's not such a bad approach.
           | 
           | I do think that LLM C code if made with great testing tooling
           | in concert has great promise.
        
             | jacquesm wrote:
             | That generalizes to anything LLM related.
        
           | lelanthran wrote:
           | > It needed an extensive fuzzing to get the minimal amount of
           | confidence, which caught at least two serious problems---
           | seriously, it's much better than most C programmers, but
           | still.
           | 
           | How are you doing your fuzzing? You need either valgrind (or
           | compiler sanitiser flags) in the loop for a decent level of
           | confidence.
        
             | lifthrasiir wrote:
             | The "minimal" amount of confidence, not a decent level of
             | confidence. You are completely right that I need much more
             | to establish anything higher than that.
        
         | citbl wrote:
         | The irony is also that AI could have been used to audit the
         | code and find these issues. All the author had to do was to
         | question.
        
         | nurettin wrote:
         | > should never see production use.
         | 
         | I have an issue with high strung opinions like this. I wrote
         | plenty of crappy delphi code while learning the language that
         | saw production use and made a living from it.
         | 
         | Sure, it wasn't the best experience for users, it took years to
         | iron out all the bugs and there was plenty of frustration
         | during the support phase (mostly null pointer exceptions and db
         | locks in gui).
         | 
         | But nobody would be better off now if that code never saw
         | production use. A lot of business was built around it.
        
           | zdragnar wrote:
           | Buggy code that just crashes or produces incorrect results
           | are a whole different category. In C a bug can compromise a
           | server and your users. See the openssl heart bleed
           | vulnerability as a prime example.
           | 
           | Once upon a time, you could put up a relatively vulnerable
           | server, and unless you got a ton of traffic, there weren't
           | too many things that would attack it. Nowadays, pretty much
           | anything Internet facing will get a constant stream of
           | probes. Putting up a server requires a stricter mindset than
           | it used to.
        
           | jacquesm wrote:
           | There are minimum standards for deployment to the open web. I
           | think - and you're of course entirely free to have a
           | different opinion - that those are not met with this code.
        
             | nurettin wrote:
             | Yes, I have lots of opinions!
             | 
             | I guess the question at spotlight is: At what point would
             | your custom server's buffer overflow when reading a header
             | matter and would that bug even exist at that point?
             | 
             | Could a determined hacker get to your server without even
             | knowing what weird software you cooked up and how to
             | exploit your binary?
             | 
             | We have a lot of success stories born from bad code. I mean
             | look at Micro$oft.
             | 
             | Look at all the big players like discord leaking user
             | credentials. Why would you still call out the little fish?
             | 
             | Maybe I should create a form for all these ahah.
        
       | lelanthran wrote:
       | I can't completely blame the language here: anyone "coding" in a
       | language new to them using an LLM is going to have real problems.
        
         | OneLessThing wrote:
         | It's funny the author says this was 90% written without AI, and
         | that AI was mostly used for the json code. I think they're just
         | new to C.
         | 
         | Trust me I love C. Probably over 90% of my lifetime code has
         | been written in C. But python newbies don't get their web
         | frameworks stack smashed. That's kind of nice.
        
           | lelanthran wrote:
           | > But python newbies don't get their web frameworks stack
           | smashed. That's kind of nice.
           | 
           | Hah! True :-)
           | 
           | The thing is, smashed stacks are _difficult_ to exploit
           | deterministically or automatically. Even heartbleed, as
           | widespread as it was, was not a guaranteed RCE.
           | 
           | OTOH, an exploit in a language like Python is almost
           | certainly going to be easier to exploit deterministically.
           | Log4j, for example, was a _guaranteed_ exploit and the skill
           | level required was basically _" Create a Java object"_.
           | 
           | This is because of the ease with which even very junior
           | programmers can create something that appears to run and work
           | and not crash.
        
         | uyzstvqs wrote:
         | It's a double-sided coin. LLMs are probably the best way to
         | learn programming languages right now. But if you vibecode in a
         | programming language that you don't understand, it's going to
         | be a disaster sooner or later.
         | 
         | This is also the reason why AI will not replace any actual jobs
         | with merit.
        
       | messe wrote:
       | > Another interesting choice in this project is to make lengths
       | signed:
       | 
       | There are good reasons for this choice in C (and C++) due to
       | broken integer promotion and casting rules.
       | 
       | See: "Subscripts and sizes should be signed" (Bjarne Stroustrup)
       | https://open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1428r0...
       | 
       | As a nice bonus, it means that ubsan traps on overflow (unsigned
       | overflows just wrap).
        
         | uecker wrote:
         | I do not agree that the integer promotion or casting (?) rules
         | are broken in C. That some people make mistakes because they do
         | not know them is a different problem.
         | 
         | The reason you should make length signed is that you can use
         | the sanitizer to find or mitigate overflow as you correctly
         | observe, while unsigned wraparound leads to bugs which are
         | basically impossible to find. But this has nothing to do with
         | integer promotion and wraparound bugs can also create bugs in -
         | say - Rust.
        
           | OneLessThing wrote:
           | It's interesting to hear these takes. I've never had problems
           | catching unsigned wrap bugs with plain old memory sanitizers,
           | though I must admit to not having a lot of experience with
           | ubsan in particular. Maybe I should use it more.
        
             | jacquesm wrote:
             | I've had some fun reviewing some very old code I wrote
             | (1980's) to see what it looked like to me after such a long
             | time of gaining experience. It's not unlike what the OP did
             | here, it reads cleanly but I can see many issues that
             | escaped my attention at the time. I always compared C with
             | a very fast car: you can take some corners on two wheels
             | but if you make a habit of that you're going to end up in a
             | wall somewhere. That opinion has not changed.
        
               | uecker wrote:
               | I think the correct comparison is a sharp knife. It is
               | extremely useful and while there is a risk it is fully
               | acceptable. The idea that we should all use plastic
               | knifes because there are often accidents with knifes is
               | wrong and so is the idea that we use should abandon C
               | because of memory safety. I follow computer security
               | issues for several decades, and while I think we should
               | have memory safety IMHO the push and arguments are
               | completely overblown - and they are especially not worth
               | the complexity and issues of Rust. I never was personally
               | impacted by a security exploit caused by memory safety or
               | know anybody in my personal vicinity who was. I know many
               | cases where people where affected by _other_ kinds of
               | security issues. So I think those are what we should
               | focus on first. And having timely security updates is a
               | hell lot more important than memory safety, so I am not
               | happy that Rust now makes this harder.
        
               | jacquesm wrote:
               | That's an interesting point you are making there. The
               | most common exploits are of the human variety. Even so it
               | is probably a good idea to minimize the chances of all
               | kinds of exploits. One other problem - pet peeve of mine
               | - is that instead of giving people _just_ security
               | updates manufacturers will happily include a whole bunch
               | of new and  'exciting' stuff in their updates that in
               | turn will (1) introduce new security issues and (2) will
               | inevitably try to extract more money from the updaters.
               | This is extremely counterproductive.
        
               | simonask wrote:
               | I'm sorry, but there is an incredible amount of hard data
               | on this, including the number of CVEs directly
               | attributable to memory safety bugs. This is publicly
               | available information, and we as an industry should take
               | it seriously.
               | 
               | I don't mean to be disrespectful, but this cavalier
               | attitude towards it reads like vaccine skepticism to me.
               | It is not serious.
               | 
               | Programming can be inconsequential, but it can also be
               | national security. I know which engineers I would trust
               | with the latter, and they aren't the kind who believe
               | that discipline is "enough".
        
               | jacquesm wrote:
               | So what do you propose to do?
        
               | simonask wrote:
               | I propose that we start taking the appropriate amount of
               | professional responsibility.
               | 
               | That includes being honest about the actual costs of
               | software when you don't YOLO the details. Zero UB is
               | table stakes now - it didn't use to be, but we don't live
               | in that world anymore.
               | 
               | It's totally fine to use C or whatever language for it,
               | but you are absolutely kidding yourself if you think the
               | cost is less than at least an order of magnitude higher
               | than the equivalent code written in Rust, C#, or any
               | other language that helps you avoid these bugs. Rust even
               | lets you get there at zero performance cost, so we're
               | down to petty squabbles about syntax or culture - not
               | serious.
        
               | jacquesm wrote:
               | > I propose that we start taking the appropriate amount
               | of professional responsibility.
               | 
               | I agree. For me that means: software _engineering_ should
               | start taking the same attitude to writing software that
               | structural engineers bring to the table when they talk
               | about bridges, buildings and other structures that will
               | have people 's lives depending on them. I'm not sure how
               | we're going to make rings out of bits but we need to
               | realize - continuously - that the price of failure is
               | often paid in blood, or in the best case with financial
               | loss and usually not by us. And in turn we should be
               | enabled to impose that same ethic on management, because
               | more often than not that's the root cause of the problem.
               | 
               | > That includes being honest about the actual costs of
               | software when you don't YOLO the details.
               | 
               | Does that include development cost?
               | 
               | Maintenance costs?
               | 
               | Or just secondary costs?
               | 
               | Why the focus on costs?
               | 
               | > Zero UB is table stakes now - it didn't use to be, but
               | we don't live in that world anymore.
               | 
               | This is because 'Rust and C# exist'? Or is it because
               | Java, Erlang, Visual Basic, Lisp etc exist?
               | 
               | > It's totally fine to use C or whatever language for it,
               | but you are absolutely kidding yourself if you think the
               | cost is less than at least an order of magnitude higher
               | than the equivalent code written in Rust, C#, or any
               | other language that helps you avoid these bugs.
               | 
               | We were talking about responsibility first, and that goes
               | well beyond just measuring 'cost'. The mistake in
               | bringing cost into it is that cost is a business concept
               | that is used to justify picking a particular technology
               | over another. And just like security is an expense that
               | doesn't show anything on the balance sheet if it works
               | besides that it cost money the same goes for picking a
               | programming language eco-system.
               | 
               | So I think focusing on cost is a mistake. That just
               | allows the bookkeeper to make the call and that call will
               | often be the wrong one.
               | 
               | > Rust even lets you get there at zero performance cost,
               | so we're down to petty squabbles about syntax or culture
               | - not serious.
               | 
               | The debate goes a lot further than that. You have
               | millions of people that are writing software every day
               | that are not familiar with Rust. To get them to pick a
               | managed language over what they are used to is going to
               | take a lot of convincing.
               | 
               | It starts of with ethics, and I don't think it should
               | start off with picking a favorite language. You educate,
               | show by example and you deliver at or below the same cost
               | that those other eco-systems do and then you slowly eat
               | the world because your projects are delivered on-time,
               | with provably lower real world defects and hopefully at a
               | lower cost.
               | 
               | And then I really couldn't care what language was picked,
               | in the rust world that translates into 'anything but C'
               | because that is perceived to be the enemy somehow, which
               | is strange because there are many alternatives to rust
               | that are perfectly suitable, have much higher mind share
               | already.
               | 
               | C is - even today - at 10x the popularity that rust is,
               | it will take a massive amount of resources to switch
               | those people over, and likely it will take more than one
               | generation. In the meantime all of the C code in the
               | world will have to be maintained, which means there is
               | massive job security for people learning C. For people
               | learning rust to the exclusion of learning C that
               | situation is far worse. This needs to be solved.
               | 
               | These are not 'petty squabbles' about syntax or culture.
               | They are the harsh reality of the software development
               | world at large, which has seen massive projects deployed
               | at scale developed with those really bad languages full
               | of undefined behavior (well, that's at least one thing
               | that Assembly Language has going for it, as long as the
               | CPU does what it says in the book undefined behavior
               | doesn't exist). People are going to point at that and say
               | 'good enough'. And they see all those memory overflows,
               | CVEs etc as a given, and they realize that in spite of
               | all of those the main vector for security issues is
               | people, and configuration mistakes not so much the
               | software itself.
               | 
               | This is not ideal, obviously, but C, like any bad habit,
               | is very hard to dislodge if your main argument is 'you
               | should drop this tool because mine is better'. Then you
               | need to _show_ that your tool is better, so much better
               | that it negates the cost to switch. And that 's a very
               | tall order, for any programming language, much more so
               | for one that is struggling for adoption in the first
               | place.
        
               | simonask wrote:
               | Cost is a useful metric because it reflects a number of
               | relevant things: Time to develop, effort to maintain -
               | yes, but also people turnover, required expertise levels,
               | satisfaction, and so on. Whether or not you like it, you
               | have to care about cost if you want to make rational
               | decisions. I'm not talking about assigning a
               | Euro/Dollar/Yuan value to each hour spent on a project,
               | but you need a rough idea about the size of the time and
               | energy investment you are making when starting a project.
               | 
               | > This is because 'Rust and C# exist'? Or is it because
               | Java, Erlang, Visual Basic, Lisp etc exist?
               | 
               | Things have changed for three important reasons: (1)
               | C/C++ compilers have evolved, and UB is significantly
               | more catastrophic than it was in the 90s and early 00s.
               | (2) As societies digitize, the stakes are higher than
               | even - leaking personal data has huge legal and moral
               | consequences, and system outages can have business-
               | killing financial consequences. (3) There are actual,
               | viable alternatives - GC is no longer a requirement for
               | memory safety.
               | 
               | > To get them to pick a managed language over what they
               | are used to is going to take a lot of convincing.
               | 
               | Perhaps you didn't mean to say so, but Rust is not a
               | managed language (that's a .NET term referring to C#, F#,
               | etc.).
               | 
               | Me and other Rust users are obviously trying to convince
               | even more people to use the language, and that's because
               | we are having a great time over here. It's a very
               | pleasant language with a pleasant community and a high
               | level of technical expertise, and it allows me to get
               | significantly closer to living up to my own ideals. I'm
               | not making a moral argument here, trying to say that you
               | or anyone is a bad person for not using Rust, but I am
               | making a moral argument saying that _denying_ the huge
               | cost and risk associated with developing software in C
               | and C++ is bullshit.
               | 
               | > And then I really couldn't care what language was
               | picked, in the rust world that translates into 'anything
               | but C' because that is perceived to be the enemy somehow,
               | which is strange because there are many alternatives to
               | rust that are perfectly suitable, have much higher mind
               | share already.
               | 
               | The point here is that, until Rust came along, you had
               | the choice between wildly risky (but fast) C and C++
               | code, or completely safe (but slow) garbage collected
               | languages with heavy runtimes and significant deployment
               | challenges.
               | 
               | C is certainly not "the enemy" - I never said that, and I
               | wouldn't. But that old world is gone. The excuse of
               | picking risky, problem-riddled languages that we _know_
               | are associated with extreme costs for reasons of
               | performance no longer has any technical merit. There can
               | be other reasons, but this isn 't it.
               | 
               | > C is - even today - at 10x the popularity that rust is,
               | it will take a massive amount of resources to switch
               | those people over [...]
               | 
               | It's insane to me that anyone would limit themselves to a
               | single language. Every competent programmer I know knows
               | at least a handful. Why are we worried about this? I'm a
               | decent C programmer, and a very good C++ programmer -
               | better at both because I'm also fairly good at Rust.
               | 
               | > And they see all those memory overflows, CVEs etc as a
               | given, and they realize that in spite of all of those the
               | main vector for security issues is people, and
               | configuration mistakes not so much the software itself.
               | 
               | "Pobody's nerfect." I'm sorry, I really dislike this
               | attitude. We can't let the fact that security is hard, or
               | that perfection is unattainable, be an excuse to deliver
               | more crap.
               | 
               | > This is not ideal, obviously, but C, like any bad
               | habit, is very hard to dislodge if your main argument is
               | 'you should drop this tool because mine is better'
               | 
               | Again, that's not my argument. My argument is that you
               | should be honest about what the actual costs, or
               | alternatively the actual quality.
        
               | jacquesm wrote:
               | > Cost is a useful metric because it reflects a number of
               | relevant things: Time to develop, effort to maintain -
               | yes, but also people turnover, required expertise levels,
               | satisfaction, and so on. Whether or not you like it, you
               | have to care about cost if you want to make rational
               | decisions. I'm not talking about assigning a
               | Euro/Dollar/Yuan value to each hour spent on a project,
               | but you need a rough idea about the size of the time and
               | energy investment you are making when starting a project.
               | 
               | You are missing the cost to switch and that's a massive
               | one and the one that I think most parties are using to
               | decide whether or not to stick with what they know or to
               | try something that is new to them. If you have a team of
               | 50 embedded C++ developers and a deadline 'let's use
               | rust' is a gamble very few managers will make.
               | 
               | > Things have changed for three important reasons: (1)
               | C/C++ compilers have evolved, and UB is significantly
               | more catastrophic than it was in the 90s and early 00s.
               | 
               | That depends on what industry you are looking at. For
               | instance, in aviation the cost of undefined behavior,
               | crashing software or wrong calculations was always that
               | high. The difference is that in that industry (and a
               | handful of others) there is enough budget to do it right
               | resulting in far fewer in production issues than what we
               | have come to accept in the 'always online, auto-update'
               | world. That whole attitude is as much or more to blame
               | for this than any particular language.
               | 
               | > (2) As societies digitize, the stakes are higher than
               | even - leaking personal data has huge legal and moral
               | consequences, and system outages can have business-
               | killing financial consequences.
               | 
               | Show me the names of the businesses that have died
               | because of data leaks or UB. See, the problem is that for
               | those businesses it usually is just a speedbump. They
               | don't care and no matter what the size of the breach the
               | consequences are usually minor.
               | 
               | The employee sticking a USB drive found on the street
               | into their laptop causing a cryptolocker incident is a
               | much more concrete problem.
               | 
               | > (3) There are actual, viable alternatives - GC is no
               | longer a requirement for memory safety.
               | 
               | GC is a convenience, and if you're going to switch
               | languages you might as well pick one that is is more
               | convenient. Java for instance is suitable now for 90% or
               | so of the use cases where C or C++ would be your only
               | option 15 years ago.
               | 
               | > Perhaps you didn't mean to say so, but Rust is not a
               | managed language (that's a .NET term referring to C#, F#,
               | etc.).
               | 
               | I know, but Java, Lisp and so on _are_ managed languages,
               | and they offer both safety _and_ convenience. Rust only
               | offers safety, other than that it is only marginally more
               | convenient than C and some would argue less so.
               | 
               | > Me and other Rust users are obviously trying to
               | convince even more people to use the language, and that's
               | because we are having a great time over here.
               | 
               | Show, don't tell.
               | 
               | > It's a very pleasant language with a pleasant community
               | and a high level of technical expertise, and it allows me
               | to get significantly closer to living up to my own
               | ideals.
               | 
               | Yes, but those are _your_ ideals, which don 't
               | necessarily overlap with mine. I don't particularly care
               | about one programming language or another, I've learned
               | enough of them by now to know that _all_ of them have
               | their limitations, their warts, their good bits and their
               | bad bits. I also know that the size of the eco-system is
               | a large function in whether or not I 'll be able to get
               | through the day in a productive way.
               | 
               | > I'm not making a moral argument here, trying to say
               | that you or anyone is a bad person for not using Rust,
               | but I am making a moral argument saying that denying the
               | huge cost and risk associated with developing software in
               | C and C++ is bullshit.
               | 
               | See, your use of the word 'bullshit' triggers me in a way
               | that you probably do not intend, but it is exactly that
               | attitude that turns me off the language that you would
               | like me to switch to. I don't particularly see that huge
               | cost and risk as applied to myself because I'm not
               | currently writing code that is going to be part of some
               | network service. If I see an embedded shop doing their
               | work in Rust then I'm happy because I can ignore at least
               | one small aspect of the source of bugs in such software.
               | But there are plenty remaining and Rust - no matter what
               | you think - is not a silver bullet for all of the things
               | that can go wrong with low level software. There are
               | other, better alternatives for most of those
               | applications, I'd be more inclined to use Java or Erlang
               | if those are available, and Go if they are not. The speed
               | at which I can develop software is a massive factor in
               | that whole 'cost' evaluation for me.
               | 
               | > The point here is that, until Rust came along, you had
               | the choice between wildly risky (but fast) C and C++
               | code, or completely safe (but slow) garbage collected
               | languages with heavy runtimes and significant deployment
               | challenges.
               | 
               | That just isn't true. There are more languages besides
               | Rust that allow for low level and fast work. Go for
               | instance is an excellent contender. And for long running
               | processes Java is excellent, it is approaching C levels
               | of throughput and excels at networked services.
               | 
               | > C is certainly not "the enemy" - I never said that, and
               | I wouldn't. But that old world is gone.
               | 
               | Sorry, but this is not a realistic stance. That old world
               | is not gone, and it is likely here to stay for many more
               | decades. There is so much inertia here in terms of
               | invested capital that you can't just make declarations
               | like these and expect to be taken serious.
               | 
               | > The excuse of picking risky, problem-riddled languages
               | that we know are associated with extreme costs for
               | reasons of performance no longer has any technical merit.
               | There can be other reasons, but this isn't it.
               | 
               | Do you realize that this is just your opinion and not a
               | statement of fact?
               | 
               | > It's insane to me that anyone would limit themselves to
               | a single language.
               | 
               | 'Insane' is another very loaded word. Is this really the
               | kind of language you want to be using while advocating
               | for Rust? There are many programmers that learn one eco
               | system well enough to carve out a career for themselves,
               | and I'm not going to be the one to judge them for that.
               | I'm not one of them, but I can see how it happens and I
               | would definitely not label everybody that's not a
               | polyglot as not entirely right in the head.
               | 
               | > Every competent programmer I know knows at least a
               | handful.
               | 
               | I know some _very_ competent programmers that only know
               | one. But they know that one better than I know any of the
               | ones that I 'm familiar with. For instance, I know a guy
               | that decided early on that if nobody wants to work on
               | COBOL projects that that is exactly what he's going to
               | do: become a world class expert in COBOL to help maintain
               | all that old stuff. At a price. He's making very good
               | money with that, far more than he'd have ever made by
               | going with something more popular. I know plenty of Java
               | only programmers and a couple that have decided that
               | python is all they need. That's _their right_ and it isn
               | 't up to me to look down on them or call them incompetent
               | because they can do something that I apparently can't:
               | focus, and get really good at one thing.
               | 
               | > Why are we worried about this? I'm a decent C
               | programmer, and a very good C++ programmer - better at
               | both because I'm also fairly good at Rust.
               | 
               | I would not label myself as 'very good' in any language,
               | I always hope to get better and in spite of doing this
               | for 4+ decades I have never felt that I was 'good
               | enough'.
               | 
               | > "P[sic]obody's nerfect." I'm sorry, I really dislike
               | this attitude.
               | 
               | Again, why the antagonism. We have many different classes
               | of issues, and depending on the context some of them may
               | not be a problem at all. I've built stuff in _JavaScript_
               | because it was the most suitable for the job. But I stay
               | the hell away from node and anything associated with it
               | because I don 't consider myself qualified to audit all
               | of the code that could be pulled in through a dependency.
               | And that's a good chunk of this: just know your
               | limitations, and realize that not just 'nobody's perfect'
               | but also that _you yourself_ are not perfect and more
               | than likely to mess up when you go into territory that is
               | unfamiliar to you.
               | 
               | > We can't let the fact that security is hard, or that
               | perfection is unattainable, be an excuse to deliver more
               | crap.
               | 
               | Ok. So now you are labeling what other people produce as
               | 'crap'. This isn't helping.
               | 
               | > Again, that's not my argument. My argument is that you
               | should be honest about what the actual costs, or
               | alternatively the actual quality.
               | 
               | So I'm not honest. If you are wondering what I meant when
               | I wrote earlier that it is the attitude of some of the
               | Rust advocates that turns me off then here in this thread
               | you have a very nice example of that. All of this
               | pontification and emotionally laden language serves
               | nobody, least of all Rust.
               | 
               | If you want to win people over try the following:
               | 
               | - refrain from insulting your target audience
               | 
               | - respect the fact that your opinions are just that
               | 
               | - understand that there may be factors outside of your
               | view that are part of the decision making process
               | 
               | - understand that you may not have a complete
               | understanding of the problem domain or the restrictions
               | involved (is a variation on the previous one)
               | 
               | - try to not use emotional language to make your point
               | 
               | - showing beats telling any day of the week
        
             | uecker wrote:
             | GCC's sanitizer does not catch unsigned wraparound. But the
             | bigger problem is that a lot of code is written where it
             | assumes that unsigned wraps around and this is ok. So you
             | you would use a sanitizer you get a lot of false positives.
             | For signed overflow, one can always consider this a bug in
             | portable C.
             | 
             | Of course, if you consistently treat unsigned wraparound as
             | a bug in your code, you can also use a sanitizer to screen
             | for it. But in general I find it more practical to use
             | signed integers for everything except for modular
             | arithmetic where I use unsigned (and where wraparound is
             | then expected and not a bug)
        
           | messe wrote:
           | I meant implicit casting, but I guess that really falls under
           | promotion in most cases where it's relevant here (I'm on a
           | train from Aarhus to Copenhagen right now to catch a flight,
           | and I've slept considerably less than usual, so apologies if
           | I'm making some slight mistakes).
           | 
           | The issues really arise when you mix signed/unsigned
           | arithmetic and end up promoting everything to signed
           | unexpectedly. That's usually "okay", as long as you're not
           | doing arithmetic on anything smaller than an int.
           | 
           | As an aside, if you like C enough to have opinions on
           | promotion rules then you might enjoy the programming language
           | Zig. It's around the same level as C, but with much nicer
           | ergonomics, and overflow traps by default in
           | Debug/ReleaseSafe optimization modes. If you want explicit
           | two's complement overflow it has +%, *% and -% variants of
           | the usual arithmetic operations, as well as saturating +|,
           | *|, -| variants that clamp to [minInt(T), maxInt(T)].
           | 
           | EDIT to the aside: it's also true if you hate C enough to
           | have opinions on promotion rules.
        
             | jacquesm wrote:
             | Yes, this is one of the more subtle pitfalls of C. What
             | helps is that in most contexts the value of 2 billion is
             | large enough that a wraparound would be noticed almost
             | immediately. But when it isn't then it can lead to very
             | subtle errors that can propagate for a long time before
             | anything goes off the rails that is noticed.
        
             | uecker wrote:
             | I prefer C to Zig. IMHO all the successor languages throw
             | out the baby with the bathwater and add unnecessary
             | complexity. But Zig is much better than Rust, but, still, I
             | would never use it for a serious project.
             | 
             | The "promoting unexpectedly" is something I do not think
             | happens if you know C well. At least, I can't remember ever
             | having a bug because of this. In most cases the promotion
             | prevents you from having a bug, because you do not get
             | unexpected overflow or wraparound because your type is too
             | small.
             | 
             | Mixing signed and unsigned is problematic, but I see issues
             | mostly in code from people who think they need to use
             | unsigned when they shouldn't because they heard signed
             | integers are dangerous. Recently I saw somebody "upgrading"
             | a C code basis to C++ and also changing all loop variables
             | to size_t. This caused a bug which he blamed on working on
             | the "legacy C code" he is working on, although the original
             | code was just fine. In general, there are compiler warnings
             | that should catch issues with sign for conversions.
        
               | lelanthran wrote:
               | > Recently I saw somebody "upgrading" a C code basis to
               | C++ and also changing all loop variables to size_t. This
               | caused a bug which he blamed on working on the "legacy C
               | code" he is working on, although the original code was
               | just fine.
               | 
               | I had the same experience about 10 years back when a
               | colleague "upgrade" code from using size_t to `int`; on
               | that platform (ATMEGA or XMEGA, not too sure now) `int`
               | was too small, overflowed and bad stuff happened in the
               | field.
               | 
               | The only takeaway is "don't needlessly change the size
               | and sign of existing integer variables".
        
           | Sukera wrote:
           | Could you expand on how these wraparound bugs happen in Rust?
           | As far as I know, integer overflow panics (i.e. aborts) your
           | code when compiled in debug mode, which I think is often used
           | for testing.
        
           | 01HNNWZ0MV43FF wrote:
           | > That some people make mistakes because they do not know
           | them is a different problem.
           | 
           | We can argue til we're blue in the face that people should
           | just not make any mistakes, but history is against us -
           | People will always make mistakes.
           | 
           | That's why surgeons are supposed to follow checklists and
           | count their sponges in and out
        
           | bringbart wrote:
           | >while unsigned wraparound leads to bugs which are basically
           | impossible to find.
           | 
           | What?
           | 
           | unsigned sizes are way easier to check, you just need one
           | invariant:
           | 
           | if(x < capacity) // good to go
           | 
           | Always works, regardless how x is calculated and you never
           | have to worry about undefined behavior when computing x. And
           | the same invariant is used for forward and backward loops -
           | some people bring up i >= 0 as a problem with unsigned, but
           | that's because you should use i < n for backward loops as
           | well, The One True Invariant.
        
         | user____name wrote:
         | I just put assertions to check the ranges of all sizes and
         | indices upon function entry, doubles as documentation, and I
         | mostly don't have to worry about signedness as a result.
        
         | kstenerud wrote:
         | Yup, unsigned math is just nasty.
         | 
         | Actually, unchecked math on an integer is going to be bad
         | regardless of whether it's signed or unsigned. The difference
         | is that with signed integers, your sanity check is simple and
         | always the same and requires no thought for edge cases:
         | `if(index < 0 || index > max)`. Plus ubsan, as mentioned above.
         | 
         | My policy is: Always use signed, unless you have a specific
         | reason to use unsigned (such as memory addresses).
        
           | bringbart wrote:
           | unsigned is easier: 'if(index >= max)' and has fewer edge
           | cases because you don't need to worry about undefined
           | behavior when computing index.
        
           | lelanthran wrote:
           | > The difference is that with signed integers, your sanity
           | check is simple and always the same and requires no thought
           | for edge cases: `if(index < 0 || index > max)`
           | 
           | Wait, what? How is that easier than `if (index > max)`?
        
       | bluetomcat wrote:
       | Good C code will try to avoid allocations as much as possible in
       | the first place. You absolutely don't need to copy strings around
       | when handling a request. You can read data from the socket in a
       | fixed-size buffer, do all the processing in-place, and then
       | process the next chunk in-place too. You get predictable
       | performance and the thing will work like precise clockwork.
       | Reading the entire thing just to copy the body of the request in
       | another location makes no sense. Most of the "nice" javaesque
       | XXXParser, XXXBuilder, XXXManager abstractions seen in "easier"
       | languages make little sense in C. They obfuscate what really
       | needs to happen in memory to solve a problem efficiently.
        
         | 01HNNWZ0MV43FF wrote:
         | Can you do parsing of JSON and XML without allocating?
        
           | bluetomcat wrote:
           | Yes, you can do it with minimal allocations - provided that
           | the source buffer is read-only or is mutable but is unused
           | later directly by the caller. If the buffer is mutable, any
           | un-escaping can be done in-place because the un-escaped
           | string will always be shorter. All the substrings you want
           | are already in the source buffer. You just need a growable
           | array of pointer/length pairs to know where tokens start.
        
           | gritzko wrote:
           | Yep, no problem. In place parsing only requires a stack.
           | Stack length is the maximum JSON nesting allowed. I have a C
           | dialect exactly like that.
        
           | veqq wrote:
           | Of course. You can do it in a single pass/just parse the
           | token stream. There are various implementations like:
           | https://zserge.com/jsmn/
        
             | andrepd wrote:
             | It requires manual allocation of an array of tokens. So it
             | needs a backing "stack vector" of sorts.
             | 
             | And what about escapes?
        
           | Ygg2 wrote:
           | Theoretically yes. Practically there is character escaping.
           | 
           | That kills any non-allocation dreams. Moment you have "Hi
           | \uxxxx isn't the UTF nice?" you will probably have to
           | allocate. If source is read-only you have to allocate. If
           | source is mutable you have to waste CPU to rewrite the
           | string.
        
             | lelanthran wrote:
             | > Moment you have "Hi \uxxxx isn't the UTF nice?" you will
             | probably have to allocate.
             | 
             | Depends on what you are doing with it. If you aren't
             | displaying it (and typically you are not in a server
             | application), you don't _need_ to unescape it.
        
               | mpyne wrote:
               | And this is indeed something that the C++ Glaze library
               | supports, to allow for parsing into a string_view
               | pointing into the original input buffer.
        
             | deaddodo wrote:
             | I'm confused why this would be a problem. UTF-8 and UTF-16
             | (the only two common unicode subsets) are a maximum of 4
             | bytes wide (and, most commonly, 2 in English text). The
             | ASCII representation you gave is 6-bytes wide. I don't know
             | of many ASCII unicode representations that have less
             | bytewidth than their native Unicode representation.
             | 
             | Same goes for other characters such as \n, \0, \t, \r, etc.
             | All half in native byte representation.
        
             | topspin wrote:
             | > Practically there is character escaping
             | 
             | The voice of experience appears. Upvoted.
             | 
             | It is conceivable to deal with escaping in-place, and thus
             | remain zero-alloc. It's hideous to think about, but I'll
             | bet someone has done it. Dreams are powerful things.
        
           | lelanthran wrote:
           | > Can you do parsing of JSON and XML without allocating?
           | 
           | If the source JSON/XML is in a writeable buffer, with some
           | helper functions you can do it. I've done it for a few small-
           | memory systems.
        
         | lock1 wrote:
         | Why does "good" C have to be zero alloc? Why should "nice"
         | javaesque make little sense in C? Why do you implicitly assume
         | performance is "efficient problem solving"?
         | 
         | Not sure why many people seem fixated on the idea that using a
         | programming language must follow a particular approach. You can
         | do minimal alloc Java, you can simulate OOP-like in C, etc.
         | 
         | Unconventional, but why do we need to restrict certain
         | optimizations (space/time perf, "readability", conciseness,
         | etc) to only a particular language?
        
           | bluetomcat wrote:
           | Because in C, every allocation incurs a responsibility to
           | track its lifetime and to know who will eventually free it.
           | Copying and moving buffers is also prone to overflows, off-
           | by-one errors, etc. The generic memory allocator is a smart
           | but unpredictable complex beast that lives in your address
           | space and can mess your CPU cache, can introduce undesired
           | memory fragmentation, etc.
           | 
           | In Java, you don't care because the GC cleans after you and
           | you don't usually care about millisecond-grade performance.
        
             | jstimpfle wrote:
             | No. Look up Arenas. In general group allocations to avoid
             | making a mess.
        
           | lelanthran wrote:
           | > Why does "good" C have to be zero alloc?
           | 
           | GP didn't say "zero-alloc", but "minimal alloc"
           | 
           | > Why should "nice" javaesque make little sense in C?
           | 
           | There's little to no indirection in idiomatic C compared with
           | idiomatic Java.
           | 
           | Of course, in both languages you can write unidiomatically,
           | but that is a great way to ensure that bugs get in and never
           | get out.
        
         | lelanthran wrote:
         | > Good C code will try to avoid allocations as much as possible
         | in the first place.
         | 
         | I've upvoted you, but I'm not so sure I agree though.
         | 
         | Sure, each allocation imposes a new obligation to track that
         | allocation, but on the downside, passing around already-
         | allocated blocks imposes a new burden for each call to ensure
         | that the callees have the correct permissions (modify it,
         | reallocate it, free it, etc).
         | 
         | If you're doing any sort of concurrency this can be hard to
         | track - sometimes it's easier to simply allocate a new block
         | and _give_ it to the callee, and then the caller can forget all
         | about it (callee then has the obligation to free it).
        
           | 1718627440 wrote:
           | To reduce the amount of allocation instead of:
           | struct parsed_data * = parse (...);         struct
           | process_data * = process (..., parsed_data);         struct
           | foo_data * = do_foo (..., process_data);
           | 
           | you can do                   parse (...) {             ...
           | process (...);             ...         }              process
           | (...) {             ...             do_foo (...);
           | ...         }
           | 
           | It sounds like violating separation of concerns at first, but
           | it has the benefit, that you can easily do procession and
           | parsing in parallel, and all the data can become readonly.
           | Also I was impressed when I looked at a call graph of this,
           | since this essentially becomes the documentation of the whole
           | program.
        
       | jqpabc123 wrote:
       | Reads like an indictment of vibe coding.
       | 
       | LLMs are fundamentally probabilistic --- not deterministic.
       | 
       | This basically means that anything produced this way is highly
       | suspect. And this framework is an example.
        
       | erichocean wrote:
       | Give Fil-C a try, the speed hit is pretty minimal and you get
       | full memory safety.
       | 
       | https://fil-c.org/
        
         | Karrot_Kream wrote:
         | Wow this is really cool, I'd never seen this before. Thanks!
        
       | dang wrote:
       | Recent and related:
       | 
       |  _Show HN: I built a web framework in C_ -
       | https://news.ycombinator.com/item?id=45526890 - Oct 2025 (208
       | comments)
        
       | yipikaya wrote:
       | As an aside, it's amusing that it took 25 years for C coders to
       | embrace the C99 named struct designator feature:
       | HttpParser parser = {             .isValid = true,
       | .requestBuffer = strdup(request),             .requestLength =
       | strlen(request),             .position = 0,         };
       | 
       | All the kids are doing it now!
        
         | 1718627440 wrote:
         | This is nice for constant data, but strdup can return NULL
         | here, which is again never checked.
         | 
         | > it took 25 years for C coders to embrace the C99 named struct
         | designator feature
         | 
         | Not sure if this actually true, but this is kind of the feature
         | of C, 20 years old code or compiler is supposed to work just
         | fine, so you just wait for some time to settle things. For fast
         | and shiny, there is Javascript.
        
       | ge96 wrote:
       | Long as you allocate me, it's alright
        
       ___________________________________________________________________
       (page generated 2025-10-10 23:00 UTC)