> Safe programming languages solve the easy problems: memory leaks, use-after-free errors, array overruns, etc. Safe languages provide no help beyond ordinary C code in solving the rather more difficult problem of computing a correct answer to an SQL statement.

I see where the author is coming from, but I don't think this is quite true. The way that safe programming languages work is that they have a richer type system that knows about the semantic context of variables, which in turn is a tool that helps a lot with the "more difficult problems".

For instance, one of the tools Rust uses for enforcing memory safety (data races and use-after-free, in particular) is that there's a distinction between "mutable" and "constant" references. But this is, really, a distinction between unique and shared references. If I am statically guaranteed the only holder of a reference to X, I can modify it; if some other part of the code might have a reference to X, I cannot.

This is essentially a readers/writer lock enforced at compiled time, and it therefore is a pattern that makes it much easier to use actual readers/writer locks: the lock-for-read function gets you a shared reference and the lock-for-write function gets you an immutable one. And Rust makes it easy to say, you cannot unlock the lock (in either variant) until you return the reference, and you cannot accidentally leak the reference out of the scope of the lock.

If you're using a readers-writer lock on, say, the schema of a table (many simultaneous readers can use a table, but only one task can alter the table and nothing else can touch the table while it's being altered), having the tools to meaningfully distinguish the cases and enforce that your mutable references don't get copied does actually make it easier to compute the correct answer to a SQL statement that's running at roughly the same time as an ALTER TABLE.

Another of the tools Rust uses is tagged unions: a C construct like union U {char x; int y;} would be memory-unsafe, so Rust is obligated to forbid it in safe code. Instead, you get an enum type that C would describe something like struct U {int tag; union {char x; int y;};}. If tag == 0, then you're on the first variant; if tag == 1, then you're on the second variant, and the compiler ensures that (in safe code) tag is never equal to anything else, never uninitialized, etc. And there are a bunch of language constructs to assist with this - for instance, the 'match' keyword lets you write cases for each variant, allowing access to x only if tag == 0 and y only if tag == 1. You're not allowed to access x or y directly at all outside of a match keyword or equivalent (like 'if let'), because that would defeat memory safety.

But because you've got good syntax and compiler-checked support for handling tagged unions, you may as well use it for problems even if you don't care about memory safety / security. Take this union from the SQLite source code, for instance: https://www.sqlite.org/cgi/src/file?ci=trunk&name=src/sqlite...

There are three types of tables (eTabType), normal, virtual, or view. There are three union variants with different data. Even ignoring the fact that some of the variables are pointers and others are non-pointers, the data doesn't make semantic sense when interpreted as the wrong variant. If you have code that handles just a normal table, and you extend it to handle virtual tables or views too, you will need to make sure you're not unconditionally accessing addColOffset, pFKey, or pDfltList, because the information you get will be wrong. Rust's enforcement of memory safety means it also prevents you from making this logical mistake.

I think we've been selling memory-safe languages as a tool for security, and I don't mean to detract from that argument at all - but there's also the fact that Rust and Go (and D and Vala and even modern C++) are newer languages that have been able to implement more things than C can, which in turn makes it easier to write correct programs in general.