C is deficient in features. You can't even have trivial algorithms like sort in a manner that is either type-safe or array size safe, not to speak of hashtables.
Still you can have all of that. And to be precise, you can even have the type safety and bounds checking if you just add it (which can certainly be done in a generic way), the catch is: at runtime only. You could probably debate a lot whether more language support here is a good thing. Of course I see the need, e.g. when it's about speeding up development
without sacrificing quality....
There are no exceptions [...]
Oh the great joy this ill concept is "missing"! Yep, that's "opinionated", we're currently going to great lengths at work (where most code is implemented in C#) to at least have our domain layer exception-free. Let me first show you some pretty old paper I found when we took that decision:
https://www.lighterra.com/papers/exceptionsharmful/
In a nutshell, exceptions introduce a "hidden" control flow path. In the real world, you basically have two kinds of error conditions: The expected errors (for which "business rules" exist), and these should always be handled as close as possible to where they occur. Doing that with exceptions always bears the risk to forget handling them, silently promoting them to the second category: Unexpected (and therefore "fatal") errors. The only sane way to "handle" them is: Abort whatever you are currently doing. Sure, you can use Exceptions for
that, but the handler probably won't do much more than logging the error in some way. Some languages came up with ideas how to "fix" the issue ("checked exceptions") you wouldn't have without exceptions in the first place, littering the code with ridiculous lists of exception types that
could appear on any boundary ... just gross.
Now, what's worse than just exceptions? Combining them with a language not offering managed resources. The "solution" C++ came up with to solve this issue is called RAII. It leads to creating lots of purely technical classes, just for the sake of managing resources leveraging the construction/destruction. Gross again. TBH, I didn't look much into Rust so far, no idea how Rust is dealing with these issues, pretty likely at least better than C++....
[...] or other error handling primitives
There's at least an idiomatic approach that isn't all bad: The return value is reserved for "error codes", while the actual return value is passed "by reference" in one of the arguments, typically the fist one. Sure, this has drawbacks, e.g. you might want more error information than just a number.
The idea I currently like most is the generic
Result<T>
object which can either contain a
Success<T>
or an
Error<T>
(with whatever description you might need). Sure, C doesn't offer that either, but if you really need it, it can be implemented
in C.
errno is a global variable for crying out loud (of course this comes from Unix but anyway).
That of course is a
horrific legacy. At least,
errno
was made thread safe (which implies some stupid complexity in the C lib implementing that). I just hope no sane modern code would ever add more usage of that extremely dumb idea.
The problem, however, is that almost all attempts to do better in a GC-less statically typed language have been screwed up. What is different about about Rust is that we don't yet know whether it is screwed up.
That's a very nice way to summarize it
As I wrote earlier, I
do have some concerns about integrating Rust into base, but they're not about the
language itself. It looks like one of the best attempts so far.