News for hackers. Stuff that matters.
Re:Why Is SQLite Coded in C and not Rust by jonahx on Tuesday October 14, 2025 @07:54PM (#45586398)
So is the argument that safe langs produce stuff like:

    // pseudocode
    if (i >= array_length) panic("index out of bounds")
that are never actually run if the code is correct? But (if I understand correctly) these are checks implicitly added by the compiler. So the objection amounts to questioning the correctness of this auto-generated code, and is predicated upon mistrusting the correctness of the compiler? But presumably the Rust compiler itself would have thorough tests that these kinds of checks work?

Someone please correct me if I'm misunderstanding the argument.

Re:Why Is SQLite Coded in C and not Rust by binary132 on Tuesday October 14, 2025 @08:15PM (#45586551)
I think it’s less like doubting that the given panic works and more like an extremely thorough proof that all possible branches of the control flow have acceptable behavior. If you haven’t tested a given control flow, the issue is that it’s possible that the end result is some indeterminate or invalid state for the whole program, not that the given bounds check doesn’t panic the way it’s supposed to. On embedded for example (which is an important usecase for SQLite) this could result in orphaned or broken resources.
Re:Why Is SQLite Coded in C and not Rust by thayne on Tuesday October 14, 2025 @09:02PM (#45586896)
But does it matter if that control flow is unreachable?

If the check never fails, it is logically equivalent to not having the check. If the code isn't "correct" and the panic is reached, then the equivalent c code would have undefined behavior, which can be much worse than a panic.

Re:Why Is SQLite Coded in C and not Rust by nubbler on Tuesday October 14, 2025 @10:10PM (#45587321)
In the first case, if it is actually unreachable, I would never want that code ending up in my binary at all. It must be optimised out.

Your second case implies that it is reachable.

Re:Why Is SQLite Coded in C and not Rust by thayne on Wednesday October 15, 2025 @11:38AM (#45594240)
In the first case, it often is optimized out. But the optimizer isn't perfect, and can't detect every case where it is unreachable.

If you have the second case, I would much rather have a panic than undefined behavior. As mentioned in another comment, in c indexing an array is semantically equivalent to:

    if (i < len(arr)) arr[i] else UB()
In fact a c compiler could put in a check and abort if it is out of bounds, like rust does and still be in spec. But the undefined behavior could also cause memory corruption, or cause some other subtle bug.
Re:Why Is SQLite Coded in C and not Rust by jonahx on Tuesday October 14, 2025 @09:07PM (#45586935)
> I think it’s less like doubting that the given panic works and more like an extremely thorough proof that all possible branches of the control flow have acceptable behavior.

The way I was thinking about it was: if you somehow magically knew that nothing added by the compiler could ever cause a problem, it would be redundant to test those branches. Then wondering why a really well tested compiler wouldn't be equivalent to that. It sounds like the answer is, for the level of soundness sqlite is aspiring to, you can't make those assumptions.

Re:Why Is SQLite Coded in C and not Rust by oconnor663 on Tuesday October 14, 2025 @08:02PM (#45586447)
> questioning the correctness of this auto-generated code

I wouldn't put it that way. Usually when we say the compiler is "incorrect", we mean that it's generating code that breaks the observable behavior of some program. In that sense, adding extra checks that can't actually fail isn't a correctness issue; it's just an efficiency issue. I'd usually say the compiler is being "conservative" or "defensive". However, the "100% branch testing" strategy that we're talking about makes this more complicated, because this branch-that's-never-taken actually is observable, not to the program itself but to its test suite.

Re:Why Is SQLite Coded in C and not Rust by lionkor on Tuesday October 14, 2025 @08:04PM (#45586460)
It's not like that, the compiler explicitly doesn't do compile-time checks here and offloads those to the runtime.

Rust does not stop you from writing code that accesses out of bounds, at all. It just makes sure that there's an if that checks.

Re:Why Is SQLite Coded in C and not Rust by selcuka on Tuesday October 14, 2025 @08:15PM (#45586542)
Ok, but you can still test all the branches in your source code and have 100% coverage. Those additional `if` branches are added by the compiler. You are responsible for testing the code you write, not the one that actually runs. Your compiler's test suite is responsible for the rest.

By the same logic one could also claim that tail recursion optimisation, or loop unrolling are also dangerous because they change the way code works, and your tests don't cover the final output.

Re:Why Is SQLite Coded in C and not Rust by binary132 on Tuesday October 14, 2025 @08:18PM (#45586568)
If they produce control flow _in the executable binary_ that is untested, then they could conceivably lead to broken states. I don’t believe most of those sorts of transformations cause alternative control flows to be added to the executable binary.

I don’t think anyone would find the idea compelling that “you are only responsible for the code you write, not the code that actually runs” if the code that actually runs causes unexpected invalid behavior on millions of mobile devices.

Re:Why Is SQLite Coded in C and not Rust by okanat on Wednesday October 15, 2025 @02:30PM (#45596635)
Well this way of arguing it may seem smart but it is not fully correct.

Google already ships binaries compiled with Rust in Android. They are actually system services which are more critical than SQLite storage of apps.

Moreover Rust version of SQLite can ship binaries compiled with a qualified compiler like Ferrocene: https://ferrocene.dev/en/ (which is the downstream, qualified version of standard Rust compiler rustc). In qualification process the compiler is actually checked whether it generates reasonable machine code against a strict set of functional requirements.

Most people don't compile SQLite with qualified versions of GCC either. So this exact argument actually can be turned against them.

Re:Why Is SQLite Coded in C and not Rust by unclad5968 on Tuesday October 14, 2025 @08:30PM (#45586662)
I don't see anything wrong with taking responsibility for the code that actually runs. I would argue it's that level of accountability has played a part in Sqlite being such a great project.
Re:Why Is SQLite Coded in C and not Rust by estebank on Tuesday October 14, 2025 @08:36PM (#45586691)
> You are responsible for testing the code you write, not the one that actually runs.

This is not correct for every industry.

Re:Why Is SQLite Coded in C and not Rust by foul on Tuesday October 14, 2025 @08:47PM (#45586762)
>You are responsible for testing the code you write, not the one that actually runs.

Hipp worked as a military contractor for battleships, furthermore years later SQLite was under contract under every proto-smartphone company in the USA. Under these constraints you maybe are not responsible to test what the compiler spits out across platforms and different compilers, but doing that makes the project a lot more reliable, makes it sexier for embedded and weapons.

Re:Why Is SQLite Coded in C and not Rust by tialaramex on Wednesday October 15, 2025 @08:07AM (#45591146)
I believe there's a Rust RFC for a way to write mandatory tail calls with the become keyword. So then the code is actually defined to have a tail call, if it can't have a tail call it won't compile, if it can have one then that's what you get.

Some languages I was aware of are defined so that if what you wrote could be a tail call it is. However you might write code you thought was a tail call and you were wrong - in such languages it only blows up when it recurses too deep and runs out of stack. AIUI the Rust feature would reject this code.

Re:Why Is SQLite Coded in C and not Rust by thfuran on Wednesday October 15, 2025 @04:42PM (#45598165)
>You are responsible for testing the code you write, not the one that actually runs

That's a bizarre claim. The source code isn't the product, and the product is what has to work. If a compiler or OS bug causes your product to function incorrectly, it's still your problem. The solution is to either work around the bug or get the bug fixed, not just say "I didn't write the bug, so deal with it."

Re:Why Is SQLite Coded in C and not Rust by selcuka on Wednesday October 15, 2025 @06:40PM (#45599194)
You are a better developer than me, then. I take it you have tests in your product repos that test your compiler behaviour, including optimisations that you enable while building binaries, and all third party dependencies you use. Is that accurate?

There is a difference between "gcc 4.8 is buggy, let's not use it" and "let's write unit tests for gcc". If you are suspicious about gcc, you should submit your patches to gcc, not vendor them in your own repo.

Re:Why Is SQLite Coded in C and not Rust by thfuran on Wednesday October 15, 2025 @09:54PM (#45600655)
>I take it you have tests in your product repos that test your compiler behaviour, including optimisations that you enable while building binaries, and all third party dependencies you use. Is that accurate?

Are you asking whether I write integration tests? Yes, I do. And at work there's a whole lot of acceptance testing too.

>There is a difference between "gcc 4.8 is buggy, let's not use it" and "let's write unit tests for gcc".

They're not proposing writing unit tests for gcc, only actually testing what gcc produces from their source. You know, by executing it like tests tend to do. Testing only the first party source would mean relying entirely on static source code analysis instead.

Re:Why Is SQLite Coded in C and not Rust by selcuka on Thursday October 16, 2025 @12:39AM (#45601514)
> Are you asking whether I write integration tests? Yes, I do.

Exactly. You don't need unit tests for the binary output. You want to test whether the executable behaves as expected. Therefore "rust adds extra conditional branches that are never entered, and we can't test those branches" argument is not valid.

Re:Why Is SQLite Coded in C and not Rust by lionkor on Thursday October 16, 2025 @10:36AM (#45605883)
You're right but only in software that isn't a database. In a database, when the program panic!()s, you're SOL. These extra branches panic.

If sqlite were to read one byte over the end of an array, it's unlikely to lose your data. Rust would guarantee that to lose data.

Re:Why Is SQLite Coded in C and not Rust by NobodyNada on Tuesday October 14, 2025 @08:36PM (#45586693)
> But (if I understand correctly) these are checks implicitly added by the compiler.

This is a dubious statement. In Rust, the array indexing operator arr[i] is syntactic sugar for calling the function arr.index(i), and the implementation of this function on the standard library's array types is documented to perform a bounds-check assertion and access the element.

So the checks aren't really implicitly added -- you explicitly called a function that performs a bounds check. If you want different behavior, you can call a different, slightly-less-ergonomic indexing function, such as `get` (which returns an Option, making your code responsible for handling the failure case) or `get_unchecked` (which requires an unsafe block and exhibits UB if the index is out of bounds, like C).

Re:Why Is SQLite Coded in C and not Rust by nubbler on Tuesday October 14, 2025 @10:12PM (#45587334)
Another commenter in this thread used the phrase "complex abomination" which seems more and more apt the more I learn about Rust.
Re:Why Is SQLite Coded in C and not Rust by J_Shelby_J on Wednesday October 15, 2025 @11:22AM (#45593974)
Nothing in this world is perfect, but this behavior is less of an abomination than whatever a junior dev on a timeline might write to handle this condition.
Re:Why Is SQLite Coded in C and not Rust by btilly on Tuesday October 14, 2025 @08:39PM (#45586707)
One of the things that SQLite is explicitly designed to do is have predictable behavior in a lot of conditions that shouldn't happen. One of those predictable behavior is that it does its best to stay up and running, and continuing to do the best it can. Conditions where it should succeed in doing this include OOM, the possibility of corrupted data files, and (if possible) misbehaving CPUs.

Automatic array bounds checks can get hit by corrupted data. Thereby leading to a crash of exactly the kind that SQLite tries to avoid. With complete branch testing, they can guarantee that the test suite includes every kind of corruption that might hit an array bounds check, and guarantee that none of them panic. But if the compiler is inserting branches that are supposed to be inaccessible, you can't do complete branch testing. So now how do you know that you have tested every code branch that might be reached from corrupted data?

Furthermore those unused branches are there as footguns which are reachable with a cosmic ray bit flip, or a dodgy CPU. Which again undermines the principle of keeping running if at all possible.

Re:Why Is SQLite Coded in C and not Rust by vlovich123 on Tuesday October 14, 2025 @10:44PM (#45587548)
In rust at least you are free to access an array via .get which returns an option and avoids the “compiler inserted branch” (which isn’t compiler inserted by the way - [] access just implicitly calls unwrap on .get and sometimes the compiler isn’t able to elide).

Also you rarely need to actually access by index - you could just access using functional methods on .iter() which avoids the bounds check problem in the first place.

Re:Why Is SQLite Coded in C and not Rust by OptionOfT on Wednesday October 15, 2025 @12:08AM (#45587975)
For slices the access is handled inside of the compiler: https://github.com/rust-lang/rust/blob/235a4c083eb2a2bfe8779...

I'm checking to see how array access is implemented, whether through deref to slice, or otherwise.

Re:Why Is SQLite Coded in C and not Rust by vlovich123 on Wednesday October 15, 2025 @12:10AM (#45587985)
I had Vec in mind but regardless nothing forces you to use the bounds-checked variant vs one that returns option<t>. And if you really are sure the bounds hold you can always use the assume crate or just unwrap_unchecked explicitly.
Re:Why Is SQLite Coded in C and not Rust by jemmyw on Tuesday October 14, 2025 @10:30PM (#45587469)
Keeping running if possible doesn't sound like the best strategy for stability. If data was corrupted in memory in a was that would cause a bounds check to fail then carrying on is likely to corrupt more data. Panic, dump a log, let a supervisor program deal with the next step, or a human, but don't keep going potentially persisting corrupted data.
Re:Why Is SQLite Coded in C and not Rust by hoppp on Tuesday October 14, 2025 @10:47PM (#45587570)
It still needs to detect that there is corrupted data, dump the log and the supervisor would not be the best if it was external since in some runtimes it could be missing, they just build it into it and we came full circle.
Re:Why Is SQLite Coded in C and not Rust by btilly on Wednesday October 15, 2025 @01:10AM (#45588288)
What the best strategy is depends on your use case.

The use case that SQLite has chosen to optimize for is critical embedded software. As described in https://www.sqlite.org/qmplan.html, the standard that they base their efforts on is a certification for use in aircraft. If mission critical software on a plane is allowed to crash, this can render the controls inoperable. Which is likely to lead to a very literal crash some time later.

The result is software that has been optimized to do the right thing if at all possible, and to degrade gracefully if that is not possible.

Note that the open source version of SQLite is not certified for use in aviation. But there are versions out there that have been certified. (The difference is a ton of extra documentation.) And in fact SQLite is in use by Airbus. Though the details of what exactly for are not, as far as I know, public.

If this documented behavior is not what you want for your use case, then you should consider using another database. Though, honestly, no other database comes remotely close when it comes to software quality. And therefore I doubt that "degrade as documented rather than crash" is a good reason to avoid SQLite. (There are lots of other potential reasons for choosing another database.)

Re:Why Is SQLite Coded in C and not Rust by Groxx on Wednesday October 15, 2025 @02:34AM (#45588777)
outside political definitions, I'm not sure "crash and restart with a supervisor" and "don't crash" are meaningfully different? they're both error-handling tactics, likely perfectly translatable to each other, and Erlang stands as an existence proof that crashing is a reasonable strategy in extremely reliable software.

I fully recognize that political definitions drive purchases, so it's meaningful to a project either way. but that doesn't make it a valid technical argument.

Re:Why Is SQLite Coded in C and not Rust by Izkata on Wednesday October 15, 2025 @10:42AM (#45593393)
If the cause of the crash is in any way related to the persisted data, there's a good chance you're now stuck in a crashloop.

If it can avoid crashing, other functionality may continue to work fine.

Re:Why Is SQLite Coded in C and not Rust by Groxx on Thursday October 16, 2025 @11:14AM (#45606426)
this is true for any kind of error branch - if the cause is persisted, it's going to still be there on the next iteration.
Re:Why Is SQLite Coded in C and not Rust by btilly on Wednesday October 15, 2025 @11:03AM (#45593720)
Yes, Erlang demonstrates that "crash and restart with a supervisor" is a potentially viable strategy to reliability.

But the choice is not just political. There are very meaningful technical differences for code that potentially winds up embedded in other software, and could be inside of literal embedded software.

The first is memory. It takes memory to run whatever is responsible for detecting the crash, relaunching, and starting up a supervisor. This memory is not free. Which is one of the reasons why Erlang requires at a minimum 10 MB or so of memory. By contrast the overhead of SQLite is something like half a MB. This difference is very significant for people putting software into medical devices, automotive controllers, and so on. All of which are places where SQLite is found, but Erlang isn't.

The second is concurrency. Erlang's concurrency model leaks - you can't embed it in software without having to find a way to fit Erlang concurrency in. This isn't a problem if Erlang already is in your software stack. But that's an architectural constraint that would be a problem in many of the contexts that SQLite is actually used in.

Remember, SQLite is not optimized for your use case. It is optimized for embedded software that needs to try to keep running when things go wrong. It just happens to be so good that it is useful for you.

Re:Why Is SQLite Coded in C and not Rust by jemmyw on Thursday October 16, 2025 @06:58AM (#45603886)
You're right and when I thought about it more I considered that "supervisor" isn't what I would want. Rather I'm thinking raising errors to the program that embeds sqlite so that it can decide what to do. I do have a desktop app that uses sqlite and I'd rather it raised an error than tried to recover.
Re:Why Is SQLite Coded in C and not Rust by ulfgard on Saturday October 18, 2025 @07:29AM (#45626496)
But getting back into a well-defined state which you can communicate via an error is a viable recovery strategy.

This state can be "the database is corrupt", allowing for a restore from backup, it can also be "shut down gracefully" and communicate that.

Especially for databases there is a ton to concinder: what about database entries that are not committed yet? When you run into oom, should you at least try to commit the data still in ram by freeing as much space as possible?

Re:Why Is SQLite Coded in C and not Rust by dathinab on Tuesday October 14, 2025 @09:40PM (#45587131)
no it's a (accidental) red Hering argument

sure safety checks are added but

it's ignoring that many of such checks get reliably optimized away

worse it's a bit like saying "in case of a broken invariant I prefer arbitrary potential highly problematic behavior over clean aborts (or errors) because my test tooling is inadequate"

instead of saying "we haven't found adequate test tooling" for our use case

Why inadequate? Because technically test setups can use

1. fault injection to test such branches even if normally you would never hit them

2. for many of such tests (especially array bound checks) you can pretty reliably identify them and then remove them from your test coverage statistic

idk. what the tooling of rust wrt this is in 2025, but around the rust 1.0 times you mainly had C tooling you applied to rust so you had problems like that back then.

Re:Why Is SQLite Coded in C and not Rust by sixthDot on Wednesday October 15, 2025 @08:16AM (#45591218)
Bound checks are usually conditionally compiled. That's more a kind of "contract" you'll verify during testing. In the end the software actually used will not check anything.

    #ifdef CONTRACTS
    if (i >= array_length) panic("index out of bounds")
    #endif
Re:Why Is SQLite Coded in C and not Rust by sixthDot on Wednesday October 15, 2025 @08:11PM (#45599984)
an hygienic way to handle that is often "assert", can be a macro or a built in statement.The main problem with assertions is the side-effects. The verification must be pure...