C alternatives
C's unsafety is a common concern, and a common reason to look
for alternatives. The subject is debatable, as language
comparisons tend to be in general, but here I document my
perspective and observations.
Requirements
The features of C I look for in alternatives:
- No garbage collection: straightforward and performant
compiled code, preferably simple compilation. Portability as
an implication.
- POSIX-friendly, easy to use and provide C APIs.
- Availability: stable and standardized language, stable
libraries with a choice of those, playing nicely with existing
systems and infrastructures, widely available from system
repositories. Not living inside a VM, relying on
curl |
sh
, inventing its own package management, requiring
nightly builds of everything, etc.
Features that C lacks, but are desirable and compatible (to some
extent) with its existing features:
- Memory (and other resource) safety.
- Type safety.
- Maybe a simpler language.
- Maybe aiming static analysis and formal verification
(possibly as a part of more advanced typing and a consequence
of being simpler).
Languages
- C++
-
Does not add any of the desirable features, but adds a lot of
complexity, and tends to break compatibility with C and the
rest by providing OO APIs.
- D
-
Some like it, but I have not used it myself, since it is an
object-oriented language, which I assume is a source of
complication, similarly to C++.
- Rust
-
Improves both memory safety and type safety, has decent
interfacing with C, but still unstable as of 2023, more or
less requiring
cargo(1)
(its own package manager)
to use, and that one aims to have the latest
rustc(1)
(compiler) version, not helping much if
the latest versions of libraries--or of their
dependencies--depend on a newer version than
installed. Idiomatic asynchronous APIs aim Rust-only sources,
not to be called via C FFI. This breaks the "POSIX-friendly"
and "availability" features present in C, though they could
have been preserved, and it has more to do with the
infrastructure than the core language. Related: Rust without
crates.io. Though some libraries (crates) are rather
lightweight, the difference in the numbers of dependencies of
similar ones can be two orders of magnitude
(e.g., argparse
has no dependencies, while the
more popular clap
pulls 156 additional
packages). Or one can add dependencies using
the --precise
option, first finding a version
compatible with an older rustc
version on
crates.io, and manually resolve all the dependency versions so
that they work together, doing the package manager's job. The
package index updates are very slow.
- Forth
-
Generally easy to interface with C code, though perhaps not to
provide a C API, and it is not safer.
- Zig
-
Not in Debian repositories, and not sure how much safer it is
than C with a little static analysis. Apparently no advanced
typing, but at least it supports generics, and sum types via
compiler-checked tagged unions (though no "extern C" for
tagged unions yet). And it aims interfacing with C. I should
look more into it later.
- Assembly
-
Sometimes C is called a "portable assembly", but it may be
practical to compile actual assembly languages for simpler
architectures, such as RISC-V, into others as well. Probably
higher-level languages, including C, are still more practical
than the nicer ones among assembly languages for simpler
architectures, but possibly the latter may compete.