> writing C code that does not crash all the time is significantly harder/expensive
This is just plain not true. The fact of the matter is that if you stick to good practices, writing C code that does not crash under regular usage is not at all difficult. The problem is that writing C code that is guaranteed not to contain memory issues is extremely hard. But writing programs that don't crash "all the time" is not at all difficult.
This is why I can understand the push to use memory-safe languages to avoid memory safety issues in critical or widely-relied-upon programs. But the main reason you are using it in these cases is to avoid memory safety bugs, not to help you write programs that don't "crash all the time". And even then, the main incentive is that memory safety bugs can be quite severe bugs in areas like networking code, where they might become a significant security risk. The crashes aren't even the main concern here.
It is also not "extremely difficult" to avoid memory-safety issues in most code, although I agree that Rust has a clear advantage. Essentially you need to have a clear policy on memory ownership and avoid pointer arithmetic by using safe buffer and string abstractions. That this often not done in practice is the issue, but I think the narrative that it is impossible in C or "extremely difficult" is more harmful than good as it shifts the blame to the language and rejects responsibility for sloppy or lazy code.
Easier said than done. In the real-world, in real projects, sustaining zero memory-safety bugs in large C codebases is exceedingly rare. Microsoft and Chrome have each reported that ~70% of their serious security bugs are memory‑safety issues, and curl attributes about 40% of its vulnerabilities to using C. Memory safety problems are really hard to avoid in practice in large C codebases. You can't just blame developers.
And, like what I was referring to, it is even more difficult to prove the absence of memory safety issues entirely. We have only managed to actually prove it for a handful of non-trivial programs, like the seL4 microkernel.
Now, this is all an entirely different question to whether the extra work required to write your program in Rust to avoid memory safety problems is actually worthwhile. If you are writing a program with no networking code, no privilege escalation, and no confidential information, the answer is probably that it doesn't matter. If a game has a buffer overflow in some weird edge case, it doesn't really matter. If your data processing code has a problem, you can probably spot that and it probably won't lead to any security concerns. If your file system scanner runs into a memory safety issue, it probably won't negatively impact you that much.
But if your crypto library has memory safety problems, you are in for a bad time.
The real-world is complex with many trade-offs. The real question if in a 1:1 comparable situation and using similar efforts, you could achieve good memory safety in C. I believe this to be the case. I think the statistics we have seen are highly biased and partially misleading and the actual differences in the number of CVEs have many reasons (including legacy code, priorities, usage scnarious, culture etc). I mean, why even mention Curl, a project with portability requirements so extreme that it sticks to a long obsolete C version (30 years!). Proving the absence of memory safety issues entirely may indeed be harder, but ensuring a reasonable safety level is quite possible in my opinion. One should also point out that only safe Rust has guaranteed absence of memory safety issues, but it is Rust - and not safe Rust - that is competing with C.
When a project grows in complexity, age, number of contributors, poor review culture, complex requirements, or any of 101 different things that can make a project hard, then it is hard to avoid memory safety bugs. This covers like 99% of non-trivial projects.
If your software projects are very simple, then it is easy to write simple C programs that do the job well. This is what I said before. If your project is simple, boring, and straight-forward, using C is just fine. It is complex projects where the use of C can become a problem (and complexity can sneak up on you from an unbelievable number of sources).
I would not say we agree with respect to your last point. I think also very complex C programs can be made memory safe, when this is a design goal from the beginning. That complexity can make this harder is true also for Rust, where people then often use unsafe, or, alternatively, re-engineer large parts of their project to get the structure right. C gives you the same choice at this point, but it may be more common to pick the unsafe path because memory safety is not valued high enough to justify the refactoring cost, so people often accept safety issues instead of fixing the design issues in the program.
This is just plain not true. The fact of the matter is that if you stick to good practices, writing C code that does not crash under regular usage is not at all difficult. The problem is that writing C code that is guaranteed not to contain memory issues is extremely hard. But writing programs that don't crash "all the time" is not at all difficult.
This is why I can understand the push to use memory-safe languages to avoid memory safety issues in critical or widely-relied-upon programs. But the main reason you are using it in these cases is to avoid memory safety bugs, not to help you write programs that don't "crash all the time". And even then, the main incentive is that memory safety bugs can be quite severe bugs in areas like networking code, where they might become a significant security risk. The crashes aren't even the main concern here.