C Is Best (2025)
sqlite.org
sqlite.org
If they are getting good results with C and without OOP, and people like the product, then those from outside the project shouldn't really have any say on it. It's their project.
There is literally nothing strange or disproportionate. It's incredibly obvious that new languages, that were designed by people who found older languages lacking, are of interest to groups of people interested in new applications of technology and who want to use their new languages.
> then those from outside the project shouldn't really have any say on it. It's their project.
People outside the project are allowed to say whatever the hell they want, the project doesn't have to listen.
To summarize, Rust provides a lot of compile-time discipline that C and C++ are lacking, and many people are tired of maintaining code that was developed without that discipline. Rust makes it harder to write low-effort software.
As a programmer that picks up a new language every 2-3 years (and one that is privileged to have an employer that tolerates this) Rust is really a breath of fresh air; not because it's easier, but because it codifies and enforces some good habits in ways that other languages don't.
> As a programmer that picks up a new language every 2-3 years (and one that is privileged to have an employer that tolerates this)
does this mean they allow you to tinker around on your own for this, or do you actually then start to deploy things to production written in the new language.
After having quite a long career as a programmer, I realised that if I were ever CTO at a startup, unless there was an absolute proven need to switch languages, I'd mandate only a single language for our (back end) stack. The cost of supporting different languages is just too high.
> you don't see Go ported to microcontrollers for instance.
AVRGo disagrees: https://github.com/avrgo-org/avrgoSurely that gap has been filled for at least a decade, even if only by Rust itself?
Moreover, I am not sure that serves as an explanation as it shows up in the strangest places. As you mention Go: Visit any discussion about Go and you'll find some arbitrary comment about Rust, even though, as you point out, they don't even exist in the same niche; being different tools for different jobs.
> Go was originally pitched as a C or C++ replacement
It was originally imagined that it would replace C++ for network servers at Google. The servers part was made abundantly clear. In fact, the team behind it expressed quite a lot of surprise that it eventually found a home doing other things as well.
> you don't see Go ported to microcontrollers for instance.
You don't? https://tinygo.org
This doesn't explain why so many rust activists are going to projects they have no involvement in and demanding they be rewritten in rust.
What's happening is that there are progressive minded people who have progressive minded tactics, where they have a cause and everywhere they go they push their cause regardless of whether the places they are going have anything to do with their cause.
For the more bulky processors, there's also tamago.
I can't wait to see results. Until now, the only real world usage was for coreutils in Ubuntu, with disastrous consequences.
Anyway, without writing the OS in Rust, Rust will always be a second tier language.
The "long history of safety issues" is actually a combination of being extremely successful (the world runs on C and C++) and production software always featuring bugs.
The moment Rust started to gain some traction, we immediately started seeing CVEs originating from Rust code.
I decided to try it for a medium-sized (~10k LoC) performance sensitive component recently and it has been an absolute joy to use.
Kotlin doesn't have a strong case for replacing java because java is, well, just fine. At least it's safe. Sure it's, like, slightly inconvenient sometimes.
And other languages like Go which originally claimed to take on C and C++ just don't. Go is garbage collected, it's not a real competition.
But Rust is different. It's actually safe, and it's actually a real competitor. There's basically zero reason to choose C other than "I know it" or "one of my library author's knows it". Which are both very good reason, but incidently have nothing to do with the language itself.
Low level memory-managed languages have been C and C++ mostly for a really long time, so I think Rust and Zig probably seem "more new" than the likes of Kotlin, Go, Elixir, Gleam, etc.
Some languages, like elixir, stick around with a low-volume, but consistently positive mention on HN. Which makes me want to use it more.
More than C++? More than Java? More than Python?
Otherwise they would have written something along the lines of "shouldn't say anything about it".
Within reason - don't be a dick and all that. :)
In my mind at least there's a decent risk Rust is going to end up like the next Haskell, its benefits other than safety are not that clear and many of those features can and have been replicated in other languages.
In my mind, the thing that makes rust and zig nice are that they put modern language features in a systems language. Non-nullable pointers and match expressions in a language that runs as fast as C? Yes please.
I love rust, but I personally doubt rust will ever be anywhere near as popular as Python, go, JavaScript and C#. It’s just so complex and difficult to learn. But its niche is pretty clear to me: I see it as a tool for writing beautiful, correct, memory safe, performant systems code. I used to love C. But between zig, rust and Odin, I can’t see myself ever using it again. C is just so much less productive and less pleasant to use than more modern languages.
And? GP didn't say that they shouldn't.
What these people do is a disservice to the general open source community, by spreading misinformation and general FUD about critical software that uses C and C++.
This is a disingenuous opinion. The level of militance involved in this propaganda push goes way beyond mere interest. Just look at people who actually enjoy Java, C++, Python, etc. These are the most popular languages that mankind ever developed,and countless people built very successful careers around them. Yet, you don't see even a fraction of the fanboys you see constantly pushing these experimental languages.
1. With Rust, you may lower the exposure, but the same classes of bug still remain. And of course, all the other non memory related bugs.
2. With C you may, if you wish, develop a big sensibility to race conditions, and stay alert. In general it is possible that C programmers have their "bugs antenna" a bit more developed than other folks.
3. With Rust, to decrease the "unsafe" sections amount, you need often to build abstractions that may be a bit unnatural.
4. Rust may create a false sense of security, and in the unsafe sections the programmer sometimes, when reviewing the code, is falsely convinced by the mandatory SAFETY comment. Like in the Linux Kernel bug, where such comment was hallucinated by a human that sometimes (not sure in this specific case, it's just an example) may be less used to do the "race spotting" process that C learns you to do.
5. With Rust, in case of a bug, the fix could no longer be the one-liner usually you see in C fixes, and can make the exposure time window larger. Sometimes fixing things in Rust means refactoring in non trivial ways.
6. With C, if there was the same amount of effort in creating wrappers to make kernel programming safer at the cost of other things, the surface of attack could also be lowered in a significant way (see for instance Redis use of sds.c: how many direct strings / pointers manipulation we avoid? The same for other stuff of course). Basically things like sds.c let you put a big part of the unsafe business in a self contained library.
So, is Rust an interesting language for certain features it has? Yes. Is Rust a silver bullet? No. So should Rust be "pushed" to others, hell no, and I suggest you to reply in the most firm way to people stressing you out to adopt Rust at all the costs.
I think the idea of developers developing a "bugs antenna" is good in theory, though in practice the kernel, Redis, and many other projects suffer from these classes of bugs consistently. Additionally, that's why people use linters and code formatters even though developers can develop a sensitivity to coding conventions (in fact, these tools used to be unpopular in C-land). Trusting humans develop sensibility is just not enough.
Specifically, about the concurrency: Redis is (mostly) single-threaded, and I guess that's at least in part because of the difficulty of building safe, fast and highly-concurrent C applications (please correct me if I'm wrong).
Can people write safer C (e.g. by using sds.c and the likes)? For sure! Though we've been writing C for 50+ years at this point, at some point "people can just do X" is no longer a valid argument. As while we could, in fact we don't.
I suppose it's possible. I wonder if I'll become a better driver if I take off my seatbelt. Or even better, if I take my son out of my car seat and just let him roam free in the back seat. I'm sure my wife will buy this.
In all seriousness, your comment reminds me of this funny video: https://www.youtube.com/watch?v=glmcMeTVIIQ
It's nowhere near a perfect analogy, but there are some striking similarities.
I think there are effects in both directions here. In C you get burned, and the pain is memorable. In Rust you get forced into safe patterns immediately. I could believe that someone who has done only Rust might be missing that "healthy paranoia". But for teaching in general, it's hard to beat frequent and immediate feedback. Anecdotally it's common for experienced C programmers to learn about some of the rules only late in their careers, maybe because they didn't happen to get burned by a particular rule earlier.
> Rust may create a false sense of security, and in the unsafe sections the programmer sometimes, when reviewing the code, is falsely convinced by the mandatory SAFETY comment.
This is an interesting contrast to the previous case. If you write a lot of unsafe Rust, you will eventually get burned. If you're lucky, it'll be a Miri failure. I think this makes folks who work with unsafe Rust extremely paranoid. It's also easier to sustain an that level of paranoia with Rust, because you hopefully only have to consider small bits of unsafe code in isolation, and not thousands of lines of application logic manipulating raw pointers or whatever.
If that were truely the case, we wouldn’t need Rust now, would we!
(4) Again, this doesn't seem to be borne out empirically.
(5) I've seen plenty of patches to C code that are way more than a single line for the Linux kernel, but sure, maybe we grant that a bug fix in Rust requires more LOC changed? It'd be nice to see evidence. Is the concern here that this will delay patching? That seems unlikely.
It's not uncommon at all for patches to the C code in the kernel for "make this generally safe" are 1000s of lines of code, seeding things like a "length" value through code, and take years to complete. I don't think it's fair to compare these sorts of "make the abstraction safe" vs "add a single line check" fixes.
(6) Also not borne out. Literally billions spent on this.
> So, is Rust an interesting language for certain features it has? Yes. Is Rust a silver bullet? No.
Agreed. I'm tempted to say that virtually no one contests the latter lol
> So should Rust be "pushed" to others, hell no, and I suggest you to reply in the most firm way to people stressing you out to adopt Rust at all the costs.
I guess? You can write whatever you want however you want, but users who are convinced that Rust code will provide a better product will ask for it, and you can provide your reasoning (as SQLite does here, very well imo) as firm as you'd please I think.
edit: And to this other comment (I'm rate limited): https://news.ycombinator.com/item?id=46513428
> made the current developers so able to reason about race conditions (also when they write Rust).
Aha. What? Where'd you get this from? Definitely not from Linus, who has repeatedly stated that lock issues are extremely hard to detect ahead of time.
> we’ve tweaked all the in-kernel locking over decades [..] and even people who know what they are doing tend to get it wrong several times
https://lwn.net/Articles/808498/
Definitely one of MANY quotes and Linus is not alone.
That’s not to say OOP advocacy has disappeared from HN. It still exists, but it no longer feels dominant or ascendant. If anything, it feels like a legacy viewpoint maintained by a sizable but aging contingent rather than a direction the community is moving toward.
Part of OOP’s staying power comes from what I’d call a cathartic trap. Procedural programming is intuitive and straightforward. OOP, by contrast, offers a new conceptual lens: objects, inheritance, polymorphism, and eventually design patterns. When someone internalizes this framework, there’s often a strong moment of “clicking” where complex software suddenly feels explainable and structured. That feeling of insight can be intoxicating. Design patterns, in particular, amplify this effect by making complexity feel principled and universally applicable.
But this catharsis is easy to confuse with effectiveness. The emotional satisfaction of understanding a system is orthogonal to whether that system actually produces better outcomes. I’ve seen a similar dynamic in religion, where the Bible’s dense symbolism and internal coherence produce a powerful sense of revelation once everything seems to “fit” together. The feeling is real, but it doesn’t validate the underlying model.
In practice, OOP often increases complexity and reduces modularity. This isn’t always obvious from inside the paradigm. It tends to become clear only after working seriously in other paradigms, where composition, data-oriented design, or functional approaches make the tradeoffs impossible to ignore.
I don't know about Zig, but my experience with Rust's trait system is that it isn't explicitly against OOP. Traits and generics feel like an extension and generalization of the OOP principles. With OOP, you have classes (types) and/or objects (instances) and bunch of methods specific to the class/object. In Rust, you extend that concept to almost all types including structs and enums.
> OOP, by contrast, offers a new conceptual lens: objects, inheritance, polymorphism, and eventually design patterns.
Rust doesn't have inheritance in the traditional sense, but most OOP languages prefer composition to data inheritance. Meanwhile, polymorphism, dynamic dispatch and design patterns all exist in Rust.
If only programming languages (or GenAI) were tools like hammers and augers and drills.
Even then the cabinets you see that come out of shops that only use hand tools are some of the most sturdy, beautiful, and long lasting pieces that become the antiques. They use fewer cuts, less glue, avoid using nails and screws where a proper joint will do, etc.
In reality, this is not the case. Bad code is the result of bad developers. Id rather have someone writing C code that understands how memory bugs happen rather than a Rust developer thinking that the compiler is going to take care of everything for them.
If the alternative has drawbacks (they always do) or is not as well known by the team, it's perfecly fine to keep using the tool you know if it is working for you.
People who incessantly try to evangelise their tool/belief/preferences to others are often seen as unpleasant to say the least and they often achieve the opposite effect of what they seek.
The US government recently called on everyone to stop using them and move to memory-safe languages.
Regardless, there are practices and tools that significantly help produce safe C code and I feel like more effort should be spent teaching C programmers those.
Edit: Typos and to make the point that I'm not necessarily defending C, just acknowledging it's place. I haven't written a significant amount of C in over 2 decades, probably, aside from microcontroller C.
C cannot be made safe (at scale). It's like asbestos. In fact, C is a hazardous material in exactly the same way as asbestos. Naturally occurring, but over industrialized and deployed far too widely before its dangers were known. Still has its uses but it will fuck you up if you do not use industrial-grade PPE.
Stop using C if you can. Stop arguing other people should use it. There have always been alternatives and the opportunity cost of ecosystems continuing to invest in C has massive externalized costs for the entire industry and society as a whole.
For that matter, there are a number of compiled memory-safe and safer languages: Dlang, Vlang, Golang, etc... who could be discussed and are equally viable choices. And if we are talking about something that needs to be outright safety-critical, Ada and SPARK should definitely be in the debate.
However, all of that doesn't override the right of projects to decide on what language they believe is best for them or what strategies concerning safety that they wish to pursue.
The US government also _really_ (no sarcasm) cares about safety-critical code that can be formally verified, depending on program requirements. DO-178, LOR1, el. al.
Developing those toolchains costs tens of millions, getting them certified costs tens of millions, and then buying those products to use costs 500k-1.5m a pop.
Those toolchains do not exist for rust. I am only aware of these toolchains existing for C, and old C at that.
Hell, rust doesn't even have a formal spec, which is a bit of a roadblock.
How would the three letter agencies then spy on people and deliver their payloads on various target devices?
The governments around the world really need the security holes to exist despite what they say.
SQLite is an example of an extremely high quality software project. That encompasses not only the quality of the software itself, but the project management around it. That includes explaining various design choices, and this document explaining the choice of language is just one of many such explanations.
You won't find easily Zig programmers that want you to use Zig at all costs, or that believe it's a moral imperative that you do. It's just antithetical to the whole concept of Zig.
The worst that can happen is that Zig programmers want C projects to have a build.zig so they can cross-compile the project trivially, since that's usually not a thing C/C++ build scripts tend to offer. And even then, we have https://github.com/allyourcodebase/ so that Zig users can get their build.zig scripts without annoying project maintainers.
One example is null-- a billion dollar mistake as Tony Hoare called it. A Maybe type with exhaustive pattern matching is so dramatically better, it can be worth switching just for that feature alone.
ML is not some new development, it just took this long to get some of its ideas mainstream.
Yeah this super common. Great comment.
Maybe writing about it was taken as an opportunity to clarify their own thinking about the topic?
Value semantics is the hot thing now I'd say.
That’s an understatement.
My only complaint would be that there's many SQL features I want to use which aren't supported. Surely some part of that is deliberately restricted scope, but some might also be dev velocity issues.
DuckDB (C++) can be used like an SQLite with more SQL features, but it's also a lot buggier. Segfaults from your database are not what you want.
So still hoping for some alternative to come along (or maybe I'll need to write it myself)
The language itself features interesting ideas, many of them borrowed (pun intended) from Haskell, so not that new after all. But the community behavior proved consistently abysmal. A real put off.
It's disingenuous to lump them together. It is the former that does the whole toxic, pushy advocacy routine.
All that said, some have found reasons to rewrite in Rust, and are currently working on that: https://turso.tech/blog/introducing-limbo-a-complete-rewrite...
If you don’t need that, then great—Turso/Limbo might be for you! But there are a ton of use cases out there that rely on SQLite for simultaneous multiprocess access. And I’m not even talking about things that use it from forking servers or for coordination (though those are surprisingly common as well)—instead, lots of processes that use SQLite 99.9% of the time from one process still need it to be multiprocess-authoritative for e.g. data exports, “can I open two copies of an app far enough to get a ‘one is already running’ error?”-type use cases, extensions/plugins, maintenance scripts, etc. not having to worry about cross-process lock files thanks to SQLite is a significant benefit for those.
The issue is more of the object-relational impedance mismatch that happens when using any SQL database: ORMs can be slow / bloated, and hand-written SQL is time consuming.
I shipped a product on SQLite, and SQLite certainly lived up to its promise. What would have been more helpful was if it could index structured objects instead of rows, and serialize / deserialize the whole object. People are doing this now by putting JSON into SQLite. (Our competitors did it when I looked into their SQLite database.)
https://github.com/tursodatabase/turso
I could see it being useful for pure Rust projects once its completed. I mean in Java / Kotlin land, I prefer to use H2 in some cases over SQLite since its native to the platform and H2 is kind of nice altogether. I could see myself only using this in Rust in place of SQLite if its easy to integrate and use on the fly.
I also think it's important to have really solid understandings (which can take a few decades I imagine) to understand the bounds of what Rust is good at. For example, I personally think it's unclear how good Rust can be for GUI applications.
Linus also brought this up: https://lkml.org/lkml/2021/4/14/1099
ul.threadlist li:hover > a {
color: red;
}
ul.threadlist li.origin > a {
display: block;
background: rgb(205, 216, 216);
font-weight: normal;
padding: 1px 6px;
margin: 1px -6px;
} ul.threadlist li.origin a {
display: block;
background: rgb(205, 216, 216);
font-weight: normal;
padding: 1px 6px;
margin: 1px -6px;
}One of the very strange things about C is that it is designed by a committee that is inherently conservative and prefers to not add new features, especially if they have any chance of breaking any compatibility. This seems necessary before Rust ever becomes an old, boring language.
But I don't see Rust ever going in such a direction. It seems fundamentally opposed to Rust's philosophy, which is to find the best solution to the problems it's trying to solve, at any cost, including breaking compatibility, at least to some degree.
Consider python2 and python3, you don't need to update your python2 code really, you can just use the python2 interpreter.
There’s also automated migration tools to convert 2021 code to 2024. It might fail on some translations but generally it’s pretty automatic.
So huge difference both in the migration mechanism and the bc story.
Rust editions only (and rarely!) break your code when you decide to upgrade your project's edition. Your public API stays the same as well (IIRC), so upgrading edition doesn't break your dependents either -unless they don't have a new enough version of the compiler to support said newer edition.
But the Rust team found a great way to avoid breaking backward compatibility: old code gets automatically compiled by the old version of the compiler, whereas more recent code is treated with the latest version of the compiler.
That is much better IMHO than carrying that old baggage along in the language that e.g. the C++ folks struggle with, where every half-decade a pile of additive changes get piled on the previous version of the language, and nobody can clean up the old mess.
That's not what happens. You always use the same version of the compiler. It's just that the newer compiler version also knows several older dialects (known as editions) of the language.
But in all the cases I can think of, when you look at that Rust today, what it meant in say 2018 Edition seems silly, "Oh, that's a daft thing for that to mean, I'm glad it doesn't mean that now"
We can't magically step into a time machine and fix how it worked at the time, any more than we could go back and cancel those minstrel shows which now seem awful. We can only fix it now and Rust's editions enable this, without the cost of making old code not work in old projects.
for dog in array_of_dogs.into_iter() {
/* In Rust 1.0 we get an immutable reference to each dog from the array */
/* But today (since 2021 Edition) we get the actual dogs, not references - thus consuming the array */
}
One of the changes I'm looking forward to from a future Edition is what happens when I write native ranges like 1..=10 (the integers 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10). Today this means core::ops::RangeInclusive but that type isn't Copy, because it is Iterator. Instead hopefully one day it'll become core::range::RangeInclusive which is Copy, and so can't be Iterator but instead IntoIterator.So in that future Rust edition (when/if it happens) I can treat 1..=10 the same way as a pair of numbers (1,10) or an array of two numbers [1, 10] which are both Copy, that is, by just copying the bit pattern you are guaranteed to get an object which means the same thing, making life easier for programmers and the compiler. Today, because it isn't Copy, I must do extra busy work, which is slightly annoying and discourages use of this otherwise great type.
The fact that C was effectively "born old" means you can take a C89 program and compile it as C23 and it should simply work, with extremely minimal changes, if any.
That's a killer feature when you're thinking in decades. Which SQLite is.
There has always been undefined behavior in C, but back in the day, compilers were nowhere near as aggressive in taking advantage of it to make your code faster. Most C programmers tended to treat C as portable assembly and not a language with rules that needed to be respected for it to not blow up your code.
I remember this being endlessly groused over by grognard, traditionalist C programmera, and more than a few of them switched to C++ as a result. After all, if the language was going to be a stickler about correctness, they might as well use a language with the features that meant they didn't have to reach into that bag of tricks.
I don't know where you got this impression but our switches from 2018 to 2021 and now 2024 editions went very smootly. Rust hasn't broken backwards compatibility in any bigger way since 1.0.
Some of its actual weirdnesses seem no less odd than that to people who aren’t C experts, I assure you.
To illustrate the difference look at C++, it was designed by a person with strong opinions, but then left it to be controlled by a committee.
If you look at the structure of C++ in Backus Naur form it is absolutely nuts. The compile times have gone through the roof and people leave to create new languages like jai, Zig or whatever.
Common Lisp was designed by committee. It is as chaotic as C++.
Rust is also gigantic. I am very careful not to use in critical systems because it is so big unless we could restrict it with the compiler or something.
This comparison confuses me because C is... also controlled by a committee? The evolution of the C standard is under the control of ISO WG14 [0], much like how the C++ standard is under the control of ISO WG21 [1]. This was true for even the first versions of each language that was standardized.
You can always use no-std if you so choose where the language is about the size of C (but still has better utility) although if you’re complaining about the size of the language, c++ is drastically worse in my opinion. Rust starting to work its way into the Linux kernel should be a signal that the language is better suited for paring down than c++
I sometimes wonder if what happens is like this:
1. Have problem. Need higher level computer language. 2. Think about problem. 3. Solve problem - 'C' 4. Think about problems with 'C' 5. Attempt to fix problems with 'C' - get C++ 6. Think about problems with C & C++ 7. Get: Go, F#, Rust, Java, JavaScript, Python, PHP, ...other etc.
I tend to do this. The problem is obvious, that I do not repeat step #2. So then I move to the next step.
8. Thinking about how to fix C, C++, Go, F#, Rust, Java, JavaScript, Python, PHP, ...other is too hard. 9. Stop thinking.
C is a Worse is Better language (https://en.wikipedia.org/wiki/Worse_is_better). The bet is that if it's simpler to implement then the fact that what you're implementing isn't great will be dwarfed by that ease of implementation. And it worked for decades which is definitely a success.
You can make a similar argument for Go, but not really for Rust. The other side of the Worse is Better coin is that maybe you could make the Right Thing™ instead.
Because implementing C is so easy and implementing the Right Thing™ is very difficult, the only way this would compete is if almost nobody needs to implement the Right Thing™ themselves. In 1970 that's crazy, each Computer is fairly custom. But by 1995 it feels a lot less "out there", the Intel x86 ISA is everywhere, Tim's crap hypermedia experiment is really taking off. And when Rust 1.0 shipped in 2015 most people were able to use it without doing any implementation work, and the Right Thing™ is just better so why not?
Now, for an existing successful project the calculation is very different, though note that Fish is an example of this working in terms of full throated RIIR. But in terms of whether you should use C for new work it comes out pretty strongly against C in my opinion as a fairly expert C programmer who hasn't written any C for years because Rust is better.
The "faster than C for general-purpose programming" is a pretty woolly claim so I'm not entirely surprised nobody claims that, but equally I think to be sure that "because none are" would need some serious caveats to make it true.
In particular you're going to have to say you think it doesn't matter how much developer time is needed. A project where C programmers spend five hours to make it run in 4 seconds is in this caveat "20% faster" than a project where Alt Lang programmers spend five minutes to make it run in 5 seconds - because only those seconds count. This is not how the real world works so it's a pretty nasty caveat.
Your example should instead be:
- 5 hours of developer time to run in 4 seconds * n
- 5 minutes of developer time to run in 5 seconds * n
As long as n <= 17,700, then the developer time is "not worthwhile." This assumes that you value user time as much as developer time.
In the case of sqlite, the user time may as well be infinite for determining that side of the equation. It's just that widely used.
But OP is correct, companies don't care as long as it doesn't translate into higher sales (or lower sales because the competitor does better). That's why you see that sort of optimization mainly in FOSS projects, which are not PDD (profits-driven development).
For any project with non-trivial amount of users the C programmer did better. I mean I know companies like to save money and move the costs (time, electricity, hardware costs) to customers but if you care about users any non-trivial speed improvement is worth a lot in aggregate, surely more than your time implementing it.
Databases are a problem space which can absorb all available effort. So if you spend five hours making thing U as fast as it could be that's five fewer hours you can dedicate to thing V or thing W, not to mention X, Y, Z and so on.
Can you expand a little?
This is such an under appreciated feature.
I'm tired of languages that just keep adding and adding to a language.
Work on the standard library or ecosystem, but stop changing the language otherwise you'll never end up anywhere because there is always going to be something that would benefit from some love.
Hell, I'd say the same is true for many libraries.
This isn't entirely true. There are a bunch of UBs that you have to be very careful about. C has the right idea about simplicity. But deterministic behavior is also important because someone will find a way to make use of UBs in their code.
Designated initializers, compound literals, binary literals and the way to printf numbers in binary, constexpr, alignas, #embed, stdbit.h, memset_explicit, restrict to name the ones I really like.
Also, the article appears to be quite old, so the submission should have a year appended.
See also previous discussions, the last of which was only a few months ago: https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...
I would strongly urge you to read and respect the HN guidelines and FAQ. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsfaq.html
This is no longer true. Rust, Zig and likely others satisfy this requirements.
> Safe languages usually want to abort if they encounter an out-of-memory (OOM) situation. SQLite is designed to recover gracefully from an OOM. It is unclear how this could be accomplished in the current crop of safe languages.
This is a major annoyance in the rust stdlib. Too many interfaces can panic (and not just in case of an OOM), and some of them don’t even document this.
Rust and Zig satisfy this by being C ABI-compatible when explicitly requested. I'm pretty sure that that solution is not actually what the author meant. When you don't explicitly use `extern "C"` in Rust or `export` in Zig, the ABI is completely undefined and undocumented. I would go as far as arguing that the ABI problem, with Rust at least, is even worse than the ABI problem C++ has. Especially since Distros are (if memory serves) starting to ship Rust crates...
Not saying they should have picked C++ but that's a bit untrue. It's quite easy given some thought into the API to invoke C++ code in basically any language which can invoke C code, since you can wrap a C++ implementation in a C interface. I've done it multiple time throughout my career (that ended up being called from Python, Swift, Objective-C/C++, Swift, Java, and Kotlin).
And as a side note, you don't have to do object-oriented programming in C++ if you don't want to.
In fact, some popular C++ libraries don't even have a stable C++ API, only a C API. LLVM comes to mind. DuckDB is another example
Is it easy to write a nice C interface for C++ that makes heavy use of templates, smart pointers, and move semantics?
I've only seen it done for C++ that is more C than C++, or for libraries that were designed to "bail out" to a C interface.
If the interface itself has or leaks those features, no that's not easy indeed. But if those do not leak, then they can be used internally yes.
My point was not that it's easy to wrap a currently existing C++ library that has modern features in its interface in a C interface, especially post-C++11.
But that if you design something from the ground up, then it's rather easy (with a certain set of constraints). My bad for not conveying that better.
I've actually run into a similar problem with a C library before, because it heavily used macros as part of its API. Glad most developers don't do that.
Classes can be wrapped with a bit of effort. You do need to write the constructors and the destructors manually and invoke a pair of new/delete on the C side, but it's just as you would handle a type allocated by a C library itself. You'd use it the same way. You just have the liberty to have the implementation use (mostly) C++.
And it's not like that's a point in favor of C, since C doesn't have vector or unique_ptr either
As someone who runs into this problem a lot, this is pretty cool! Does anyone know how they can recover from this in SQLite?
How are you running into it?
If you're writing in C, idiomatic code works (check the return values of functions that allocate!)
If you're in C++, Rust or similar, you have to write pretty non-idiomatically to recover from an allocation failure.
`new` throws, `malloc` returns. That's a pretty big difference!
Idiomatic C++ code never puts a `try` around `new`, while idiomatic C code always checks the return from an allocation.
So you have to ensure that in such situation, you can execute code that does not require more memory: ensure that the rest only free stuff, or preallocate structures for that purpose
1. Stability: Ada is an "old and boring" language (aka stable and reliable - the first standard for the language was issued in 1983 and created for the Defence industry) - https://learn.adacore.com/courses/intro-to-ada/chapters/intr...
2. Compatibility: Interoperability features is built-in, allowing you to create Ada libraries that can be used with other languages - https://piembsystech.com/interfacing-ada-programming-with-c-...
3. Embedded Programming: Ada has been designed for embedded programming and the language includes constructs for directly manipulating hardware, for example, and direct interaction with assembly language. - https://learn.adacore.com/courses/intro-to-embedded-sys-prog...
4. Ada supports processes / suites to comply with Branch coverage in ISO 26262 ( https://github.com/NVIDIA/spark-process ) and Decision coverage in DO-178C ( https://learn.adacore.com/booklets/adacore-technologies-for-... ).
5. Ada has features supporting memory safety to avoid many memory issues in the first place ( https://www.adacore.com/blog/memory-safety-in-ada-and-spark-... ) and using its exception handling and programming techniques (like pre-allocating "guard" memory) can allow Ada programs to tackle fringe OOM errors.
6. Performance: Ada is built for systems programming, is just as fast as C ( https://learn.adacore.com/courses/Ada_For_The_Embedded_C_Dev... ), with the added bonus of "modern" programming features that can generate more "reliable" code
I also noticed many software companies have been switching to SQLite for storing data. Audacity has switched from using hundreds of tiny audio files in multiple folders for a single recording to a single file using SQLite. I suspect it is the reliability that comes from sticking to C
But programming languages are like Math. It is like saying "multiplying is outdated" or "the square root is outdated".
Do you still write FORTRAN and Perl?
Many try to replace C. All failed.
I'd love to see a language that could satisfy both high end needs and low need needs. I would not know how such a language would look though.
What do you mean by all failed? Rust supports CPython modules using PyO3 [1]. Even the popular polars dataframe library [2] uses it.
Whoa. TIL there's a minimal build of SQLite that doesn't even need malloc() and free() !
Presumably for embedded devices. I'd love to see examples of that being used in the wild.
[1] https://web.archive.org/web/20170701061906/https://sqlite.or...
Let’s be serious now.
Overall, it makes sense. C is a systems language, and a DB is a system abstraction. You shouldn't need to build a deep hierarchy of abstractions on top of C, just stick with the lego blocks you have.
If the project had started in 2016, maybe they would have gone for c++, which is a different beast from what it was pre-2011.
Similarly, you might write SQLite in Rust if you started today.
That section was probably written 20 years ago when Java was all the rage.
https://web.archive.org/web/20250130053844/https://250bpm.co...
* Error handling via exceptions. Rust uses `Result` instead. (It has panics, but they are meant to be strictly for serious logic errors for which calling `abort` would be fine. There's a `Cargo.toml` option to do exactly that on panic that rather than unwinding.) (btw, C++ has two camps here for better or worse; many programs are written in a dialect that doesn't use exceptions.)
* Constructors have to be infallible. Not a thing in Rust; you just make a method that returns `Result<Self, Error>`. (Even in C++ there are workarounds.)
* Destructors have to be infallible. This is about as true in Rust as in C++: `Drop::drop` doesn't return a `Result` and can't unwind-via-panic if you have unwinding disabled or are already panicking. But I reject the characterization of it as a problem compared to C anyway. The C version has to call a function to destroy the thing. Doing the same in Rust (or C++) is not really any different; having the other calls assert that it's not destroyed is perfectly fine. I've done this via a `self.inner.as_mut().expect("not terminated")`. They say the C only has two states: "Not initialised object/memory where all the bets are off and the structure can contain random data. And there is initialised state, where the object is fully functional". The existence of the "all bets are off" state is not as compelling as they make it out to be, even if throwing up your hands is less code.
* Inheritance. Rust doesn't have it.
The only language that would make sense for a partial/progressive migration is zig, in huge part due to its compatibility with C. It's not mentioned in the article though.
Almost certainly correct. It is however being rewritten in Rust by other people https://github.com/tursodatabase/turso. This is probably best thought of as a seperate, compatible project rather than a true rewrite.
For example, Rust has additional memory guarantees when compared to C.
https://turso.tech/blog/introducing-limbo-a-complete-rewrite...
once data leaves the fs and is all (or in part) bought into a processes memory, access is just limited by the memory bandwidth.
ofcourse if you start touching uncached data things slow down, but just for a short while…
It's just the simplistic first feeling of a language, in the same way I feel like Go is the language for Kubernetes/cloud/net servers, Ruby is for Rails, JS is the browser/bootcamp language, Python is Data Science and AI...
Sure, you get that safety but you are also throwing the stability that an old (and maintained) codebase has just for the sake of being patched to handle the presssure from the real world.
Maybe it's time Rust positions itself as something more than "the language of rewrites".
This feels like they're responding to people asking for SQLite to be rewritten in Java. Who are these people?!
I believe this article is from a few years ago
https://sqlite.org/testing.html
>As of version 3.42.0 (2023-05-16), the SQLite library consists of approximately 155.8 KSLOC of C code. (KSLOC means thousands of "Source Lines Of Code" or, in other words, lines of code excluding blank lines and comments.) By comparison, the project has 590 times as much test code and test scripts - 92053.1 KSLOC.
No UB, strict types, contracts that can be formally proven, runtime checks, better (than phthreds) concurrency, certifiable.
It's bitch sometimes but you'll thank it after release.
Doesn't the language compiler write the code that checks if the array access is in-bounds? Why would you need to test the compiler's code?
...saying that for a statement `if( a>b && c!=25 ){ d++; }`, they use 100% machine-code branch coverage as a way of determining that they've evaluated this in `a<=b`, `a>b && c==25`, and `a>b && c!=25`. (C/C++) branch coverage tools I've used are less strict, only requiring that takes both if and else paths.
One could imagine a better high-level branch coverage tool that achieves this intent without dropping to the machine code level, but I'm not sure it exists today in Rust (or any other language for that matter).
There might also be an element of "we don't even trust the compiler to be correct and/or ourselves to not have undefined behavior" here, although they also test explicitly for undefined behavior as mentioned later on the page.
let val = arr[i]
to assembly code like: cmp rdx, rsi ; Compare i (rdx) with length (rsi)
jae .Lpanic_label ; Jump if i >= length
; later...
.Lpanic_label:
call core::panicking::panic_bounds_check
Are they saying with "correct code" the line of source code won't be covered? Because the assembly instruction to call panic isn't ever reached?It might be reasonable to redefine their metric as "100% branch coverage except for panics"...if you can reliably determine that `jae .Lpanic_label` is a panic jump. It's obvious to us reading your example of course but I don't know that the compiler guarantees panics always "look like that", and only panics look like that.
I don't think they thought about this too much. In the C code are they testing the "branch" into Undefined Behaviour?
2. Ada is about as old as C so should have been considered as a possible safe language. It's safer than Rust in my opinion. GNAT was released in 1995 while SQLite is from 2000, so it was a possibility. C++ was also around: still unsafe, though less so than C.
I've heard it said you shouldn't use asserts in production code, but handle errors and exceptions instead? That said, can't you just 'if !condition() {panic...}' to simulate the behavior? Or are they talking about something specific about Golang?
I think the "lingua franca" argument for C and the points at the end about what they'd need from Rust to switch do go beyond merely justifying a decision that's already been made, though.
A Rust implementation exists. It's called turso
https://github.com/tursodatabase/turso
There's a whole company around it.
This artical makes the case for C as a low level language, I then offer Javascript as a functional-ish language for manipulating domain model data structures on the top.
As a language, it's too basic. Almost every C projects try to mimic what C++ already has.
Then came the rise of FOSS adoption, with the GNU manifest asserting to use only C as compiled language.
"Using a language other than C is like using a non-standard feature: it will cause trouble for users. Even if GCC supports the other language, users may find it inconvenient to have to install the compiler for that other language in order to build your program. So please write in C."
The GNU Coding Standard in 1994, http://web.mit.edu/gnu/doc/html/standards_7.html#SEC12
First of all, ABI is a property of the OS calling conventions, which happen to overlap with C on UNIX/POSIX, given its symbiotic relationship.
Secondly, https://thephd.dev/to-save-c-we-must-save-abi-fixing-c-funct...
https://www.youtube.com/live/EIKAqcLxtT0?si=J82Us4zBlXLPbZVq
It is a matter of skill.
Because I could not, by scrubbing that video, find anything where immense skill is used to deal with the enormous overhead that standard C++ forces on every program that uses it.
it seems rather stupid to take a sub-heading as the title.
that said, just because c is best for sqlite today, doesn't mean c is best for you or what you're trying to do.
I've had many new cars and the car I drive today is a classic, I love it. Takes me everywhere, replace the parts and still goes. No rust. Can't say the same for a new car. I had a brand new truck a few years ago and I sold it. I had way too many problems. Doors being not aligned, electrical problems. Just shit all around. I had a Ford Focus ST as well for a for awhile too and I hated it. I couldn't even go in a car wash without it leaking through the roof, new car as well. Crazy!!!
Just imagine if all of Chromium was written in C and used simpler tools like GNU Make and Git for it's projects.
Edit: Dafny: A verification language capable of enforcing invariants
For someone who might not think so they may think "Why?" and click it.
I think this makes a bit more sense.
Disclaimer: Credit goes to whoever mentioned this in this discussion before me.
> All that said, it is possible that SQLite might one day be recoded in Rust.
...followed by a list of reasons why they won't do it now. I think the first one ("Rust needs to mature a little more, stop changing so fast, and move further toward being old and boring.") is no longer valid (particularly for software that doesn't need async and has few dependencies), but the other ones probably still are.
I write Rust code and prefer to minimize non-Rust dependencies, but SQLite is the non-Rust dependency I mind the least for two reasons:
* It's so fast and easy to compile: just use the `rusqlite` crate with feature `bundled`. It builds the SQLite "amalgamation" (its entire code basically concatenated into a single .c file). No need to have bazel or cmake or whatever installed, no weird library dependency chains, etc.
* It's so well-tested that the unsafety of the language doesn't bother me that much. 100% machine branch coverage is amazing.
Of course Go programmers will say "Just don't do that" but that's exactly what C programmers told us about bounds misses, use after free and so on...
Plain and simple C is, by far, one of the current _LESS WORSE_ performant alternatives which can be used, actually, from low level to large applications.
C syntax is already waaaaay to rich and complex (and ISO is a bit too much pushing feature creep over 5/10 years cycles).
>>"the spiral rule"
Not a problem in 99.99% of cases where it's just: type name = something;
with maybe * and [] somewhere.
We need a new C, fixed, leaner and less complex: primitives are all sized (u64, u8, f64, etc), only one loop primitive (loop{}), no switch, no enum, no typedef, no typeof and other c11 _generic/etc, no integer promotion, no implicit casts (except for void* and literal numbers?), real hard compiler constants, current "const" must go away (I don't even talk about "restrict" and similar), no anonymous code block, explicit differentiation between reinterpret,runtime,etc casts?, synchronous numeric computation handling, anonymous struct/union for complex memory layouts, and everything else I am currently forgetting about.
(the expression evaluation of the C pre-processor would require serious modifications too)
We need clean and clear cut definitions, in regards of ABIs, of argument passing by value of aggregates like structs/arrays, you know, mostly for those pesky complex numbers and div_t.
We need keywords and official very-likely-to-be-inline-or-hardware-accelerated-functions, something in between the standard library and the compiler keywords, for modern hardware architecture programming like memory barriers, atomics, byte swapping, some bitwise operations (like popcnt), memcpy, memcmp, memmove, etc.
In the end, I would prefer to have a standard machine ISA (RISC-V) and code everything directly in assembly, or with very high level languages (like [ecma|java]script, lua, etc) with a interpreter coded in that standard machine ISA assembly (all that with a reasonable SDK, ofc)
stdint.h already gives you that.
>> only one loop primitive (loop{}), no switch, no enum
I don't think you will find many fans of that.
>> atomics
check stdatomic.h
>>some bitwise operations (like popcnt)
Check stdbit.h in C23
I love C, when I wrote my first programs in it as a teenager, I loved the simplicity, I love procedural code. Large languages like C++ and or Rust are just too much for ME. Too much package. I like writing code in a simple way, if I can do that with a small amount of tools, I will. You can learn C and be efficient in it within a couple weeks. C systems are just built better, faster, more stable, portable, and more! This is my 2 cents.
Most honnest software developers know the answer: for any computer language with an ultra-complex syntax(c++,etc) or which requires an intrusive and tricky runtime (java,etc), the answer is obvsiously "nope". But we all know, for plain and simple C, the answer is "yes" (have a look at cproc and QBE, or scc, tinycc, etc). The new C I am talking about should reduce the effort related to such compiler developement and "fix" all C implicit stuff hiding too much things, kind of error prone.
There is a huge pitfall though: linux is not coded in plain and simple C, but in GCC C... which is VERY different, a GCC C alternative, is clancg/llvm, those are not to be called reasonable in any capacity.
If linux code would care about having clean assembly source files on top of plain and simple C code, unfortunately it would be slower. BUT, you guess what, for the ability to compile linux with an _REAL ALTERNATIVE_ C compiler (and a small assembler), I will happily accept to have a "slower" kernel.
Why they? You should do it.