|
|
|
|
|
by TuringTest
3143 days ago
|
|
> C, contrary to popular belief, actually does have a runtime I've been left wondering what you meant by this. Are you referring to the stack and heap management? Or OS processes and threads? If not, could you please explain what you mean by C runtime, and how does Rust differs from it when it is shut down?? |
|
There's two components to the C runtime, what is specified by the C standard, and what is specified by POSIX and the operating systems. I am not sufficiently familiar with the C world to tell you exactly which thing is defined in which part. Fortunately, for this discussion of how integrating C code into another runtime goes, it doesn't really matter.
The C runtime includes the assumption that there is a malloc-compatible memory allocator available (note it's swappable), the process of linking programs when they start up and the whole surrounding "symbols" they can obtain. It has certain assumptions about what state needs to be saved when a function is called; for instance, it won't save the flags on the processor controlling IEEE FPU conformity. Function calls have a "stack" and there's a "heap", and the language itself distinguishes between them. C itself, IIRC, has no specification for threads whatsoever, but the OSes seem to have converged on a fairly similar model that could be fairly called part of the runtime now.
It's hard to "see" the C runtime because it has won so thoroughly that it just looks like "how computation is done", or is so deeply integrated into the operating system that it forces parts of the model on everything that runs on that OS. You kind of have to piece together what C does by looking at what it does that other languages do differently. Yes, most programs at some point will do some linking and symbol resolution, but once the interpreter has started up, dynamic languages have no concept of a static symbol table. Loading another Python module doesn't even remotely resemble loading a C library, either at startup or dynamically later. The language Go doesn't have a stack or a heap. The implementation does for practical reasons, but the language does not. Most other languages now will save the same things on the call stack as C, but that's not a requirement of computation; you could save a lot more of the processor's state, but it'll trash your function performance to do it. A "stack" and "heap" model is not necessary; Haskell for instance does not have a clear "stack" at all. (It does stack-like things, certainly, but it turns out getting what most people call "a stacktrace" from the runtime is actually fairly hard. I believe still not possible on GHC.) There are alternate methods for threading, including models that still use the C-style threads under the hood but include mandatory code to be run at startup and shutdown to be "part" of the runtime.
C is not as thin as it looks; it's just that history has made it appear to be the baseline. And as I know my internets, let me say that nothing in this post is criticism. Something has to be the baseline. While I think the C baseline is getting long in the tooth, it won for a reason, and I don't know that we could have gotten much better from the 1970s. (The other competition usually cited was either a performance non-starter (the Lisp of the time), or had it survived for 40+ years, we'd be able to write a very similar post about how it is getting long in the tooth too in 2017 (Pascal, for instance).)