<p>Chapter 1 of &quot;The Rust Programming Language&quot; (Klabnik and Nichols) says:</p>
<blockquote>
<p>[S]ome common Rust packages depend on C code and will need a C compiler.</p>
</blockquote>
<p>Why do Rust packages have any dependency on C code? That is, why does Rust code ever need to use something that isn't also written in Rust?</p>

<c><rust>
Why do common Rust packages depend on C code?
CC BY-SA 4.0
2023-11-25T04:50:03.943Z
Is your expectation that every layer of the stack should be rewritten for every language (i.e. single-language machines), or are you thinking about the implementation of those languages themselves (in another language versus self-hosted), something specific about foreign-function interfaces & dynamic loading in Rust, properties of C, or something else? The question is fairly broad now and I can imagine e.g. the situation for Rust and Python is different, but there are a lot of question marks going in different directions too — which are the question to be answered and which are giving context?
Michael Homer
CC BY-SA 4.0
2023-11-25T16:10:14.733Z
@MichaelHomer - the angle to my question was the first from your list, i.e. "why isn't every layer of the stack rewritten for every language"? The answer from @apropos goes some way to answering this (I'm still digesting his answer), but a reliance between Rust and `libc` seems like a link-time dependency...I'm still not sure I've understood Rust's reliance on a C *compiler*.
StoneThrow
CC BY-SA 4.0
2023-11-25T17:42:54.400Z
To the extent that the question is that first point and your comment on the answer, I don’t think this *is* a PL question after all, but something about the history of Unix or computing. Single-language machines have existed in the past but are not common general-purpose systems today, and while e.g. reimplementing GTK in every language seems obviously undesirable it’s also not what you’re asking about. If you can edit it down to a *single* on-topic question I think there is something answerable in here, and maybe other separate questions too, but I’m not sure they’re what you’re looking for.
Michael Homer
CC BY-SA 4.0
2023-11-26T00:10:18.580Z
I think the close *reason* is wrong, but closing is appropriate: there are just too many different questions here. The answer to "why does Python rely on C?" is trivial: Python isn't compiled directly to machine code, and sometimes you need the performance of something that is. The answer to "why does Rust rely on C?" is probably more about pragmatism of what already exists. The answers to all of your supplemental "is it...?" questions are "yes" for some languages and "no" for others, with a side helping of "because languages are designed by people who have opinions".
IMSoP
CC BY-SA 4.0
2023-11-26T00:47:12.747Z
I too think the close is a shame. I rushed to answer it because I saw the close votes. I think someone else could have given a better answer.
apropos
CC BY-SA 4.0
2023-11-26T01:56:00.513Z
I still think this question should have remained closed, but I have edited it down to one single question that I think was indicated as being the main question. It still doesn't really seem to be about programming language design or implementation so much as overall system architecture and pragmatics, though.
Michael Homer
CC BY-SA 4.0
2023-11-26T05:21:17.867Z
99.9% of the code in the world is not written in Rust. C interop lets you use code that was written in *many* languages. Creating a useful library is a very substantial investment of time and effort, and rewriting a popular, well-written, well-maintained library just because it happens to be written in a different language is, 99.9% of the time, a thoroughly bad idea that will get you an inferior product.
hobbs
CC BY-SA 4.0
2023-11-26T12:19:36.803Z
[This question is being discussed on meta.](https://langdev.meta.stackexchange.com/q/614/89)
kaya3
CC BY-SA 4.0
2023-11-26T19:38:06.587Z
@MichaelHomer: It could fit better on Stack Overflow or https://softwareengineering.stackexchange.com/.  Agreed it's not so much about the language design but the engineering around its ecosystem, and the ecosystem of platforms it runs on.
Peter Cordes
CC BY-SA 4.0
2023-11-27T00:56:44.587Z
@MichaelHomer - having considered the sum of all the answers and comments here, I understand why this is possibly not suitable for this Stack Exchange: the answer to the question (both initial and edited) has to do with the implementation of the runtime/ecosystem/etc. of a language, not the language design itself. I wonder if there might not have been a better phrasing than the quoted text from "The Rust Programming Language" that would have clarified the nature of this "dependency" on C "code."
StoneThrow
CC BY-SA 4.0
2023-11-28T05:04:51.123Z
You mean phrasing like "depend on libraries written in C"?  Probably that's exactly what the authors meant by their phrasing, and didn't consider that it might not be obvious to all readers.
Peter Cordes
<p>(note: this answer is addressing a quite different question from the original revision: <em>why one language would have a dependency on another language?</em>)</p>
<blockquote>
<p>Is there is something fundamental about C that other languages like Rust simply cannot do?</p>
</blockquote>
<p>Yes. C is the language of the Linux kernel. <code>libc</code> is a fundamental library for interacting with the kernel, and reimplementing it in a non-C language would be a bit of a fool's errand (you'd still be interfacing with C regardless). Of course, <a href="https://users.rust-lang.org/t/clarifications-on-rusts-relationship-to-libc/56767" rel="noreferrer">you don't have to use libc to use Rust</a> for ex. embedded targets, but if you want your code to compile and run and do useful things on Linux, you're going to have to link against it. And to link against it (or anything on Linux), you'll <a href="https://github.com/rust-lang/rust/issues/39915#issuecomment-618726211" rel="noreferrer">need a C compiler</a>, and have to <a href="https://faultlore.com/blah/c-isnt-a-language/" rel="noreferrer">talk with the C ABI</a> (see <a href="https://langdev.stackexchange.com/a/3237/2303">Alexis King's answer</a> for much more in depth about <em>this</em>).</p>
<blockquote>
<p>Or is it that existing C code is so well written that there's no point reinventing those wheels in newer languages?</p>
</blockquote>
<p>Sometimes. <a href="https://github.com/curl/curl" rel="noreferrer"><code>curl</code></a> is an excellent and battle-tested library despite being written in C. I've seen a number of languages - off the top of my head, <a href="https://github.com/treeform/puppy#about" rel="noreferrer">some libraries in Nim</a> - prefer to link to curl rather than implement the layers of the networking stack themselves. Rust libraries may do the same, though I do not know for sure.</p>
<blockquote>
<p>Does it have anything to do with modern programming languages abstracting away certain primitive programming concepts like pointers/memory, thereby leaving some things &quot;impossible&quot; in the newer language?</p>
</blockquote>
<p>No. There are certainly things that are impossible to do in some programming languages: an obvious example is nontermination in languages that are total, like <a href="https://en.wikipedia.org/wiki/Agda_(programming_language)#Termination_checking" rel="noreferrer">Agda</a> (excluding bugs). If you have certain restrictions on how you want to accomplish a task - for example, if you want to return a reference in a language that disallows it like Lobster, or if you want to have a binary under X bytes in a language known for bigger binaries like Rust, then that certainly can be &quot;impossible&quot;, but quite a lot of programming languages that abstract over raw concepts like pointers and memory have some sort of escape hatch to allow you to do what you could do in an unsafe language.</p>
<blockquote>
<p>I've heard, anecdotally, that some Python packages rely on C code.</p>
</blockquote>
<p>A lot of Python packages rely on C code for performance: for example, <a href="https://github.com/numpy/numpy" rel="noreferrer"><code>numpy</code></a>. This is because Python is Really Goddamn Slow and less so due to <em>limitations</em> of Python.</p>
<blockquote>
<p>So perhaps my question is a more generic &quot;Why does language X have any dependency on C code? Why isn't it language X 'all the way down'?&quot;</p>
</blockquote>
<p>There are likely more answers to that that I'm unaware of, but you laid out the questions for the answers I know of nicely in your own post.</p>
<hr />
<p>(I welcome improvements to this answer. I'm not so confident about my knowledge of <code>libc</code>, in particular.)</p>

CC BY-SA 4.0
2023-11-25T09:19:46.587Z
2023-11-26T20:06:24.163Z
2023-11-26T20:06:24.163Z
apropos
CC BY-SA 4.0
2023-11-25T16:06:33.823Z
When you say "`libc` is a fundamental library for interacting with the kernel" do you mean functions like `open()` etc.? Where do language authors draw the line between "what's worth reinventing" and what's not? Is it that essentially, _C came early enough that it implemented the first kernel-interacting library,  i.e. `libc`, which became time-tested and battle-hardened over time, therefore it would be a "fool's errand" for newer languages to reinvent specifically this kernel-interacting library?_
StoneThrow
CC BY-SA 4.0
2023-11-25T17:24:28.680Z
@apropos You don’t need a C compiler to link against libc, though, so the first paragraph doesn’t actually give an answer. That’s especially the case for Rust which can innately produce C-compatible exports and imports itself.
Michael Homer
CC BY-SA 4.0
2023-11-25T17:30:56.873Z
@StoneThrow POSIX defines all system interactions in terms of the C interface and does not define any other kernel interaction, so libc-kernel is one thing as far as userland is concerned. Things like [`open`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html), which fundamentally interact with the system, need to be accessed that way (or by platform- and even version-specific non-standard interfaces, like numbered syscalls; Go does try to do that). The need to move in lockstep means there are very few even replacement libcs for any platform.
Michael Homer
CC BY-SA 4.0
2023-11-26T00:48:18.913Z
@MichaelHomer The point is you need *libc* to link against libc. C is a special language because libc is written in C, and libc is essentially part of the kernel interface. You certainly don't need a C compiler to link against it though, yeah, but I *think* (not sure here) that's because the C ABI is special (by virtue of being the de facto ABI).
apropos
CC BY-SA 4.0
2023-11-26T01:30:13.693Z
Yes, you do need libc to link against libc, but that isn't part of the question; the Linux kernel is one of the few things *not* to be mentioned in it! It's a gesture at an answer, but without actually being an answer.
Michael Homer
CC BY-SA 4.0
2023-11-26T01:41:10.360Z
Their question is why a language would have a dependency on another language, and one of their nicely broken down sub-questions is "is there something special about C". C is special by virtue of being the language of the Linux kernel.
apropos
CC BY-SA 4.0
2023-11-26T02:01:16.843Z
Since the question was reopened I have edited it down to be a single question rather than closing it as "Needs more focus"; you may want to update accordingly.
Michael Homer
CC BY-SA 4.0
2023-11-26T02:13:51.120Z
"C is special by virtue of being the language of the Linux kernel"; but what does Linux have to do with this programming language's packages? This is an argument the answer could make, but it doesn't, and it doesn't *obviously* connect to this need for a C compiler - it needs to explain why these are things that the Rust user needs to care about.
Michael Homer
CC BY-SA 4.0
2023-11-26T02:20:04.317Z
Linux has to do with the programming language because Linux is a Big Target for compiled code. If you want your program to run on Linux, and you don't link against `libc`, you can't do much of value. I'll update my answer to clarify.
apropos
CC BY-SA 4.0
2023-11-26T02:23:32.230Z
Also @MichaelHomer - your edit makes this a *very* different question than originally posed, I think it should be reverted. The question used Rust as an example but was very much not exclusive to Rust, it was asking what the reasons there are for a language to rely upon another language. There are a fairly small number of answers to that, which is why I think this question is in scope, and I think I hit on them all in my response - C is special, other code can be good, the host language can be slow.
apropos
CC BY-SA 4.0
2023-11-26T02:34:31.740Z
You can revert it, though the question certainly needed more focus - the mere fact that you're answering five disconnected questions illustrates that! If it can be edited into a focused question that preserves more of what you want, great. The author indicated that they were aiming at why "every layer isn't rewritten for every language", but also said the question was specific to Rust; I'm not sure how the most general version of that couldn't be unanswerably broad. This is one risk of speeding to answer a question you see is being closed, unfortunately... It can be edited further though.
Michael Homer
CC BY-SA 4.0
2023-11-26T16:29:55.400Z
It's somewhat a misnomer to say you need the C ABI to interact with the kernel. You need the implement the syscall API. Sure types/structs need the same ABI, but no function calls. Would be a lot of work (which as far as I understand is why rust doesn't do it), but wouldn't require calling into C (unless you count the kernel's syscall handler, but those have unique calling conv anyways). Also you didn't mention it here, but rust also uses libc for dynamic loading/_start/address space setup.
Noah
CC BY-SA 4.0
2023-11-26T18:42:44.093Z
@apropos - I'm still digesting your answer as well as AlexisKing's answer, and all the surrounding comments. Forgive me if I somehow misunderstood or overlooked this in your existing answer, but the impression I've gotten is that (certain) Rust packages will potentially need to _link to `libc`_, i.e. they have a dependency on a _library_ written in C, as opposed to "C _code_" as was stated in "The Rust Programming Language" -- is that correct? I.e. were the book authors (presumably) glossing over the distinction between C _code_ and a _library written in C_?
StoneThrow
CC BY-SA 4.0
2023-11-26T18:58:23.410Z
@apropos - your "you don't have to use libc to use Rust" link seems to indicate that Rust's need for a C _compiler_ is merely to invoke the linker (presumably ld) -- did I understand that correctly? If so, why use a C compiler to invoke ld? Why not just invoke ld directly?
StoneThrow
CC BY-SA 4.0
2023-11-26T20:03:07.627Z
@StoneThrow I believe that's correct. ld/lld is interwoven with a C compiler toolchain. I dug up [some more details here](https://github.com/rust-lang/rust/issues/39915#issuecomment-618726211).
apropos
CC BY-SA 4.0
2023-12-03T14:54:38.307Z
"you'd still be interfacing with C regardless" -- this is not true, on Linux it's possible to interface directly with the kernel via system calls, the ABI is stable, AOT and JIT compilers could generate code following that calling convention. "if you want your code to compile and run and do useful things on Linux, you're going to have to link against it" this is also not true, I'm working on a freestanding programming language right now, a standalone lisp interpreter written in C with zero dependencies, it's a completely static executable that doesn't link to anything including libc.
Matheus Moreira
<blockquote>
<p>Why do Rust packages have any dependency on C code?</p>
</blockquote>
<p>Taken literally, this question is not really on-topic: it’s a question about some decisions some people made when they wrote some libraries, not a question about the Rust programming language. By and large, their motivations likely have more to do with the practicalities of software engineering than anything fundamental about programming languages. Ultimately, it’s just plain <em>convenient</em> to reuse C code because there’s a lot of it, it’s particularly easy to call into, C toolchains are ubiquitous, and whatever runtime support it needs is almost certainly already loaded into your process.</p>
<p>Still, if we follow that reasoning just a little bit further, there are definitely a few programming language questions hidden in there. For example, why is C so exceptionally easy to call from other languages? Why does Rust have built-in support for C calling conventions and struct layout but not other languages? This is by no means specific to Rust: in <em>most</em> languages, “foreign function interface” might as well be a synonym for “a way to call into C”, and it is not at all uncommon for C toolchains to show up in unexpected places.</p>
<p>Why is that?</p>
<h1>C ABIs</h1>
<p>The phrase “the C ABI” is something of a misnomer, as C does not specify an ABI, nor is it really defined at the level that would allow specifying one. Rather, “the C ABI” refers to a general collection of things defined for every combination of architecture and operating system. For example:</p>
<ul>
<li><p>The <a href="https://en.wikipedia.org/wiki/Calling_convention" rel="noreferrer">calling convention</a> specifies a standard set of rules for how arguments are passed to and returned from functions, the layout of the stack, and what registers are preserved across the call.</p>
</li>
<li><p>Datatype size and alignment rules specify the necessary size and alignment of primitive types like <code>int</code>, <code>long</code>, and <code>float</code>.</p>
</li>
<li><p>Structure type layout rules specify how composite structures are laid out in memory and how they are passed to and returned from functions.</p>
</li>
<li><p>Executable formats define how executables and shared libraries should be laid out so that the operating system knows how to load them into memory, set up their virtual address space, and dynamically link them.</p>
</li>
</ul>
<p>It is easy to quibble about what is and is not part of “the C ABI” for a given platform. Are executable formats <em>really</em> a part of the C ABI, or are they something else? What about thread local variables? Signal handling? Debugging symbols and unwinding information? I’m not going to try to pin down some precise list because there simply isn’t one. What matters is two things:</p>
<ol>
<li><p>Where precisely the C ABI and C standard library end and your operating system begins is not at all clear.</p>
</li>
<li><p>It is extraordinarily important that the C ABI for a given platform does not ever change.</p>
</li>
</ol>
<p>These two things are very closely related.</p>
<h2>C is your operating system’s interface</h2>
<p>As <a href="https://langdev.stackexchange.com/a/3234">apropos’ answer</a> points out, there is a real sense in which <a href="https://faultlore.com/blah/c-isnt-a-language/" rel="noreferrer">C isn’t (just) a programming language</a>. A staggering amount of essential operating system functionality is not just <em>most convenient</em> to access via C libraries, it’s <em>the only supported way</em> of accessing it. Sure, if you really want, you can find the conventions for making a syscall “directly”, but the idea that syscalls are the interface between userland and the operating system is rather idealistic. For one, syscalls are the interface between userland and the <em>kernel</em>, and the kernel is really an exceedingly small part of even a fairly minimal modern operating system. For another, they are poorly documented and require reverse-engineering on proprietary operating systems, and worse, <em>syscalls are not even stable on Windows</em>.</p>
<p>So it is really rather black and white: the intended way to call into your operating system is to call a C function. In fact, let me make this even more explicit: it is not just the intended way for a human to write a computer program that calls into your operating system, it is the intended mechanism by which every programming language in existence calls into your operating system. On Linux, one can perhaps quibble with this (though I still think it would be quite ill-advised), but on macOS and Windows, you really cannot, so there’s just no way around it—your programming language <em>needs</em> some way to (eventually) make C calls.</p>
<p>That said, if your programming language is interpreted, you basically don’t have to worry about this problem. Whatever language your interpreter is written in already knows how to make the necessary calls, so you can just defer to that. In fact, even a compiled language that uses its own binary object format and does its own linking and loading and has its own runtime can largely get away with not knowing how to generate C calls because the program can always yield to the runtime’s scheduler, which is written in a language that knows how to perform the call into C.</p>
<p>This means that, counterintuitively, being a systems language without a runtime (like Rust) means you actually have to care <em>more</em> about the details of the C ABI on each and every platform because you have to be able to generate code that makes those calls directly. You have to speak the platform’s calling convention, you have to do all your stack layout in a way that’s compatible with the C stack, you need mechanisms for generating code that uses the correct structure layout and alignment rules, and you need to figure out what to do when a function written in your language returns to a function written in C, something that is not altogether obvious when your language provides functionality like exceptions that is not supported by the C ABI.</p>
<h2>C ABIs are difficult to separate from the C language</h2>
<p>After everything I said above, you hopefully understand why just about every programming language under the sun needs some way to speak the platform’s C ABI, but you might wonder whether calling it “the C ABI” is very useful. You might think it would be clearer if we called it “the platform ABI,” and in fact that is often done! This might lead you to believe that we could dispense with all the <em>programming language</em> parts of C—like type declarations and header files and <code>.o</code> objects—and just focus on the <em>binary interface</em> bits, which really don’t need to be considered through the lens of C at all.</p>
<p>Sadly, if you actually try to implement a compiler, you will rapidly discover that this ABI is painfully difficult to disentangle from the programming language C. In order to make a C call, you don’t just need to know what registers to pass arguments in and what size an <code>int</code> is, you need to know things like struct layout and what it means to spill a struct onto the stack. You also need some way to take the interface specifications provided by operating systems and turn them into a format your compiler can work with, and wouldn’t you know it, those interface specifications are provided in the form of… C header files.</p>
<p>No, really: go down this rabbit hole and it will start to drive you mad. Pick just about any syscall that involves a struct type and try to figure out how to automatically determine how to generate the right code to use it. Consider the case of <code>stat</code>. The man page says it’s in <code>sys/stat.h</code>, but how do we even find where that header is on some arbitrary system? <a href="https://stackoverflow.com/q/13079650">The highest-voted answer to a Stack Overflow question about how to do this on Linux is to call <code>gcc</code>.</a> I assure you it does not get any easier to avoid giving in and calling a C compiler from here.</p>
<p>The reality is that C header files are currently used as the lowest-level <a href="https://en.wikipedia.org/wiki/Interface_description_language" rel="noreferrer">interface description language</a> you can get your hands on, and this is a somewhat frustrating choice seeing as it is virtually impossible to parse them without writing a C compiler. But let’s suppose you did that—you deduced all the platform-specific rules for layout and calling conventions, you reimplemented the C preprocessor, and you wrote a fully-featured C parser and symbol resolver that allows you to parse any standard C header file—you will rapidly run into <em>another</em> problem.</p>
<p>The header files on your system are not written in standard C.</p>
<h2>C ABIs are difficult to separate from C toolchains</h2>
<p>This part can come as a bit of a shock to people, especially since there’s some idea that the header files found on your computer are compiler-agnostic. In fact, if you go digging through your system’s header files, you’ll find an awful lot of <code>#ifdef __GNU_C__</code> and <code>#ifdef __clang_major__</code> littered about. In practice, your header files are very much vendor-specific, they just explicitly handle a small list of known vendors.</p>
<p>At this point it may become clear how hopeless this all is. The deeper you look, the fuzzier the line between your operating system and the C compiler becomes. In a way, the entire C toolchain is a part of the operating system, the wildly complex decoder ring you are forced to use if you want to  be able to conform to the eye-wateringly elaborate machinations required to call into your operating system. Yes, I realize this might sound hysterical, but consider just how deeply the C toolchain is interwoven with languages that, frankly, would rather have as little to do with it as possible:</p>
<ul>
<li><p>Rust compiles to LLVM, and <a href="https://github.com/rust-lang/rust-bindgen/" rel="noreferrer">rust-bindgen</a> uses libclang.</p>
</li>
<li><p>GHC tries its best to implement a common subset of the platform calling convention for its FFI, but users found this insufficient enough that <a href="https://downloads.haskell.org/ghc/9.8.1/docs/users_guide/exts/ffi.html#the-capi-calling-convention" rel="noreferrer">it now supports a mode that just generates a <code>.c</code> source file containing a stub function and compiles it with a C compiler</a>.</p>
</li>
<li><p>The Swift compiler straight-up <a href="https://llvm.org/devmtg/2014-10/Slides/Skip%20the%20FFI.pdf" rel="noreferrer">includes an embedded libclang</a>.</p>
</li>
</ul>
<p>There will always be people who quixotically struggle to avoid depending on the C toolchain at all costs, but doing so is essentially hopeless. Even if one were to implement everything required to consume system header files without MSVC, GCC, or Clang, support for those headers is such a moving target that it would likely create a headache-inducing maintenance burden. What’s more, C’s exceptional ubiquity only entrenches it further: when software written in different languages needs to coexist in the same process, the mediator is the collection of languages, tools, runtime infrastructure, and operating system support that we now know under the moniker “C”. Why not something else? Because everything already supports C. So everything has to support C.</p>
<p>To quote the <a href="https://twitter.com/__phantomderp" rel="noreferrer">current Twitter bio</a> of JeanHeyd Meneide, current Project Editor for the C standardization committee:</p>
<blockquote>
<p>The C Standard Cannot Be Replaced And Will Never Be Destroyed.</p>
</blockquote>
<p>Quite.</p>

CC BY-SA 4.0
2023-11-26T07:35:31.663Z
2023-11-26T23:42:40.927Z
2023-11-26T23:42:40.927Z
Alexis King
CC BY-SA 4.0
2023-11-26T13:39:16.343Z
(re. "C does not specify an ABI, nor is it really defined at the level that would allow specifying one"), C (the standard library) does pretty much define the storage format for strings, though, and that does end up as part of the ABI for any OS calls that expect e.g. a filename from the caller.
ilkkachu
CC BY-SA 4.0
2023-11-26T16:26:49.010Z
"There will always be people who quixotically struggle to avoid depending on the C toolchain at all costs, but doing so is essentially hopeless." I've got some projects where this was most expedient. Recent cases involved tools to repair fundamental damage to the C userspace infrastructure, so *not using it* was essential to getting the job done. Yes, this meant looking up the syscall ABI and invoking it directly.
Joshua
CC BY-SA 4.0
2023-11-26T22:14:46.360Z
...also, I got a little lost in the sentence "...a compiled language that uses its own binary object format and does its own linking and loading and has its own runtime can largely get away with not knowing how to generate code that calls into C code because a C call can always yield to the runtime’s scheduler, which is written in a language that knows how to perform the call into C" -- could you either expand on that, or maybe bring that description one step closer to lay terms? I didn't quite understand "yield[ing] to the runtime's scheduler" and what entity makes the call into C thereof...?
StoneThrow
CC BY-SA 4.0
2023-11-26T22:17:20.670Z
@StoneThrow I think that getting into that is largely out of scope for this answer, but essentially: if you have a runtime, then that runtime can always be used to assist with making calls. For example, if the language is garbage collected (using a stop-the-world collector), there fundamentally must be some way for the program to temporarily stop executing and allow the GC to do its work. This is known as “yielding to the runtime”, and that same mechanism can be used to request that the runtime make a call on the program’s behalf.
Alexis King
CC BY-SA 4.0
2023-11-26T22:31:20.240Z
@AlexisKing -- I apologize for the extended chat, I know it's frowned upon, but this Q&A has revealed many enticing rabbit holes to explore. My question was perhaps a bit more primitive than you might have interpreted: in this context, does the word "runtime" mean "stuff the language provides during the program's time of execution that was not explicitly written by the coder"? E.g. garbage-collection, from, maybe, languages like Go...the coder doesn't explicitly garbage-collect, but the language provides it -- is that, then, (part of) the language's "runtime"?
StoneThrow
CC BY-SA 4.0
2023-11-26T23:00:37.717Z
@StoneThrow Essentially: yes.
Alexis King
CC BY-SA 4.0
2023-11-26T23:16:26.207Z
Funny you mention macOS because that was the huge exception. Classic macOS was not C-based, but Pascal. The Carbon API made it more C-friendly, but most functions utilized Pascal calling, as it was needed for Macintosh Toolbox, and Pascal strings were everywhere. Carbon was finally removed in macOS 10.15 (2020).
user71659
CC BY-SA 4.0
2023-11-27T00:29:57.570Z
Old Windows was also more Pascal based than C based -- this shows up in calling conventions with `cdecl` vs `stdcall`.  C mostly came into the picture with the dominance of UNIX.
Chris Dodd
CC BY-SA 4.0
2023-11-27T01:08:51.030Z
Is it fair for me to summarize that (nearly) all programming languages' runtimes ultimately have a reliance on libc to make system calls, because libc has already implemented all the ugly/difficult/low-level work of making those system calls, e.g. which registers are used to pass values? I gather from other commenters that there are "equivalents" to libc, written in other languages like Pascal, so presumably programming language implementers could choose to "call out" to the Pascal-equivalent-of-libc if they had a foreign-function interface to Pascal?
StoneThrow
CC BY-SA 4.0
2023-11-27T08:21:30.960Z
@StoneThrow: Yes, *on UNIX* and similar systems (such as non-Android Linux). But no, on Windows and Android. Windows is especially interesting because its `COM` API's are thinly disguised C++ API's.
MSalters
CC BY-SA 4.0
2023-11-27T08:35:38.733Z
To add to the list of OSes: OpenBSD is considering restricting syscalls to libc (its own, obviously) for security reasons. The security aspect, as I understand it, being that it's easier to craft a syscall than it is to craft a call to a function from an ASLR loaded libc, and therefore mandating going through libc will make exploits more difficult to perform.
Matthieu M.
CC BY-SA 4.0
2023-11-27T23:57:17.693Z
@StoneThrow Historically yes. In modern times no though. You can’t get away from the calling conventions of the OS kernel as long as you’re using an OS, and pretty much all of them use C, C++, or some close equivalent these days. So, for example, if you were to try to use Free Pascal’s runtime libraries in place of libc for your tools, you’d still be making C ABI calls to the OS itself, if not calling into the platform’s libc routines.
Austin Hemmelgarn
CC BY-SA 4.0
2023-11-28T00:33:19.357Z
On Windows, Microsoft now maintains portable API descriptions (winmd) for Win32, COM and WinRT. The official Rust bindings for Windows are generated from these.
Alex Jasmin
CC BY-SA 4.0
2023-11-28T04:15:46.587Z
@AustinHemmelgarn - I wonder if I might be able to nudge you to expand your comment on the historical/modern aspect into an answer. I know extended chats are discouraged, but my curiosity is piqued by your statement that "if you were to try to use Free Pascal’s runtime libraries in place of libc for your tools, you’d still be making C ABI calls to the OS itself, if not calling into the platform’s libc routines" -- I don't fully understand why: I thought the point of Pascal's runtime libraries would be to be outright equivalent to libc, i.e. offer their own syscall implementation, etc.
StoneThrow
CC BY-SA 4.0
2023-11-28T05:18:13.717Z
Re: "C ABI" even though ISO C doesn't define an ABI:  The way to read that term is that it's an ABI that supports C, making compatible C implementations possible.  A C ABI defines all the functionality needed by C, but usually not much more.  e.g. it defines how to call functions with a fixed signature or C-style variadic, and to return one value, possibly an aggregate.  And sizes / alignments for the C primitive types. You can have multiple ABIs on a platform, like AArch64 64-bit vs. ILP32. Or some embedded platforms like ARM Linux have had a few different ABIs, not binary compatible.
Peter Cordes
CC BY-SA 4.0
2023-11-28T05:21:16.650Z
A C ABI doesn't have to define anything only C++ needs, like conventions for vtable layout.  A C++ ABI does that on top of a C ABI; for example `g++` uses what's called the "Itanium C++ ABI" across most (all?) platforms as its convention for name-mangling, vtables, how try/catch exception blocks find each other, and when by-value args can actually be passed by value in registers vs. when they actually get passed by pointer in terms of the C ABI so the object always has an address. (something like non-trivially-copyable)
Peter Cordes
CC BY-SA 4.0
2023-11-28T12:12:16.263Z
@StoneThrow Historically, there were systems that provided interfaces other than the C ABI as the primary or only ABI. Prominent examples include Lisp machines from various vendors, WD’s Pascal MicroEngine, and the Java Card platform. But the C ABI is what UNIX and Windows systems provide these days, so that’s what software that runs on those platforms has to use. Even if you use assembly code to make a system call on Linux, you’re still using C calling conventions and the C ABI to make the call, _because that’s the only way you can make the call_.
Austin Hemmelgarn
CC BY-SA 4.0
2023-11-28T12:24:24.470Z
@StoneThrow And even aside from the OS kernel ABI, there are quite a few things that libc makes significantly easier to work with. For example, on Linux, system call numbers vary based on what CPU architecture you’re on (as an example, `read` is 0 on 64-bit x86, 3 on 32-bit ARM, and 63 on 64-bit ARM). libc hides all of that cleanly though, so you don’t need to care as long as you’re only calling things it supports. A lot of other languages take advantage of that (among other things) when running on Linux, even if they are not inherently tied to C.
Austin Hemmelgarn
CC BY-SA 4.0
2023-12-03T15:08:21.490Z
"On Linux, one can perhaps quibble with this (though I still think it would be quite ill-advised)" For those who would enjoy quibbling with this, [I have written somewhat at length](https://www.matheusmoreira.com/articles/linux-system-calls) about it on my website. Linux system calls are stable and can be safely used directly. "it is the intended mechanism by which every programming language in existence calls into your operating system" Not _every_ language. I created a lisp interpreter with builtin syscall support. Go also does it. There are more examples but I'm running out of characters :)
Matheus Moreira
# Why do common Rust packages depend on C code?
Posted by: username_0 on 2023-11-25
Chapter 1 of "The Rust Programming Language" (Klabnik and Nichols) says:

> [S]ome common Rust packages depend on C code and will need a C compiler.

Why do Rust packages have any dependency on C code? That is, why does Rust code ever need to use something that isn't also written in Rust?
**Tags:** c rust
> **Comments:**
> **username_1**: Is your expectation that every layer of the stack should be rewritten for every language (i.e. single-language machines), or are you thinking about the implementation of those languages themselves (in another language versus self-hosted), something specific about foreign-function interfaces & dynamic loading in Rust, properties of C, or something else? The question is fairly broad now and I can imagine e.g. the situation for Rust and Python is different, but there are a lot of question marks going in different directions too — which are the question to be answered and which are giving context?  
> **username_0**: @MichaelHomer - the angle to my question was the first from your list, i.e. "why isn't every layer of the stack rewritten for every language"? The answer from @apropos goes some way to answering this (I'm still digesting his answer), but a reliance between Rust and `libc` seems like a link-time dependency...I'm still not sure I've understood Rust's reliance on a C *compiler*.  
> **username_1**: To the extent that the question is that first point and your comment on the answer, I don’t think this *is* a PL question after all, but something about the history of Unix or computing. Single-language machines have existed in the past but are not common general-purpose systems today, and while e.g. reimplementing GTK in every language seems obviously undesirable it’s also not what you’re asking about. If you can edit it down to a *single* on-topic question I think there is something answerable in here, and maybe other separate questions too, but I’m not sure they’re what you’re looking for.  
> **username_2**: I think the close *reason* is wrong, but closing is appropriate: there are just too many different questions here. The answer to "why does Python rely on C?" is trivial: Python isn't compiled directly to machine code, and sometimes you need the performance of something that is. The answer to "why does Rust rely on C?" is probably more about pragmatism of what already exists. The answers to all of your supplemental "is it...?" questions are "yes" for some languages and "no" for others, with a side helping of "because languages are designed by people who have opinions".  
> **username_3**: I too think the close is a shame. I rushed to answer it because I saw the close votes. I think someone else could have given a better answer.  
> **username_1**: I still think this question should have remained closed, but I have edited it down to one single question that I think was indicated as being the main question. It still doesn't really seem to be about programming language design or implementation so much as overall system architecture and pragmatics, though.  
> **username_4**: 99.9% of the code in the world is not written in Rust. C interop lets you use code that was written in *many* languages. Creating a useful library is a very substantial investment of time and effort, and rewriting a popular, well-written, well-maintained library just because it happens to be written in a different language is, 99.9% of the time, a thoroughly bad idea that will get you an inferior product.  
> **username_5**: [This question is being discussed on meta.](https://langdev.meta.stackexchange.com/q/614/89)  
> **username_6**: @MichaelHomer: It could fit better on Stack Overflow or https://softwareengineering.stackexchange.com/.  Agreed it's not so much about the language design but the engineering around its ecosystem, and the ecosystem of platforms it runs on.  
> **username_0**: @MichaelHomer - having considered the sum of all the answers and comments here, I understand why this is possibly not suitable for this Stack Exchange: the answer to the question (both initial and edited) has to do with the implementation of the runtime/ecosystem/etc. of a language, not the language design itself. I wonder if there might not have been a better phrasing than the quoted text from "The Rust Programming Language" that would have clarified the nature of this "dependency" on C "code."  
> **username_6**: You mean phrasing like "depend on libraries written in C"?  Probably that's exactly what the authors meant by their phrasing, and didn't consider that it might not be obvious to all readers.  
---
## 2 answers
### Answer by username_9 on 2023-11-26. Score: 105 | Accepted answer
> Why do Rust packages have any dependency on C code?

Taken literally, this question is not really on-topic: it’s a question about some decisions some people made when they wrote some libraries, not a question about the Rust programming language. By and large, their motivations likely have more to do with the practicalities of software engineering than anything fundamental about programming languages. Ultimately, it’s just plain *convenient* to reuse C code because there’s a lot of it, it’s particularly easy to call into, C toolchains are ubiquitous, and whatever runtime support it needs is almost certainly already loaded into your process.

Still, if we follow that reasoning just a little bit further, there are definitely a few programming language questions hidden in there. For example, why is C so exceptionally easy to call from other languages? Why does Rust have built-in support for C calling conventions and struct layout but not other languages? This is by no means specific to Rust: in *most* languages, “foreign function interface” might as well be a synonym for “a way to call into C”, and it is not at all uncommon for C toolchains to show up in unexpected places.

Why is that?

# C ABIs

The phrase “the C ABI” is something of a misnomer, as C does not specify an ABI, nor is it really defined at the level that would allow specifying one. Rather, “the C ABI” refers to a general collection of things defined for every combination of architecture and operating system. For example:

* The [calling convention](https://en.wikipedia.org/wiki/Calling_convention) specifies a standard set of rules for how arguments are passed to and returned from functions, the layout of the stack, and what registers are preserved across the call.
* Datatype size and alignment rules specify the necessary size and alignment of primitive types like `int`, `long`, and `float`.
* Structure type layout rules specify how composite structures are laid out in memory and how they are passed to and returned from functions.
* Executable formats define how executables and shared libraries should be laid out so that the operating system knows how to load them into memory, set up their virtual address space, and dynamically link them.

It is easy to quibble about what is and is not part of “the C ABI” for a given platform. Are executable formats *really* a part of the C ABI, or are they something else? What about thread local variables? Signal handling? Debugging symbols and unwinding information? I’m not going to try to pin down some precise list because there simply isn’t one. What matters is two things:

1. Where precisely the C ABI and C standard library end and your operating system begins is not at all clear.
2. It is extraordinarily important that the C ABI for a given platform does not ever change.

These two things are very closely related.

## C is your operating system’s interface

As [apropos’ answer](https://langdev.stackexchange.com/a/3234) points out, there is a real sense in which [C isn’t (just) a programming language](https://faultlore.com/blah/c-isnt-a-language/). A staggering amount of essential operating system functionality is not just *most convenient* to access via C libraries, it’s *the only supported way* of accessing it. Sure, if you really want, you can find the conventions for making a syscall “directly”, but the idea that syscalls are the interface between userland and the operating system is rather idealistic. For one, syscalls are the interface between userland and the *kernel*, and the kernel is really an exceedingly small part of even a fairly minimal modern operating system. For another, they are poorly documented and require reverse-engineering on proprietary operating systems, and worse, *syscalls are not even stable on Windows*.

So it is really rather black and white: the intended way to call into your operating system is to call a C function. In fact, let me make this even more explicit: it is not just the intended way for a human to write a computer program that calls into your operating system, it is the intended mechanism by which every programming language in existence calls into your operating system. On Linux, one can perhaps quibble with this (though I still think it would be quite ill-advised), but on macOS and Windows, you really cannot, so there’s just no way around it—your programming language *needs* some way to (eventually) make C calls.

That said, if your programming language is interpreted, you basically don’t have to worry about this problem. Whatever language your interpreter is written in already knows how to make the necessary calls, so you can just defer to that. In fact, even a compiled language that uses its own binary object format and does its own linking and loading and has its own runtime can largely get away with not knowing how to generate C calls because the program can always yield to the runtime’s scheduler, which is written in a language that knows how to perform the call into C.

This means that, counterintuitively, being a systems language without a runtime (like Rust) means you actually have to care *more* about the details of the C ABI on each and every platform because you have to be able to generate code that makes those calls directly. You have to speak the platform’s calling convention, you have to do all your stack layout in a way that’s compatible with the C stack, you need mechanisms for generating code that uses the correct structure layout and alignment rules, and you need to figure out what to do when a function written in your language returns to a function written in C, something that is not altogether obvious when your language provides functionality like exceptions that is not supported by the C ABI.

## C ABIs are difficult to separate from the C language

After everything I said above, you hopefully understand why just about every programming language under the sun needs some way to speak the platform’s C ABI, but you might wonder whether calling it “the C ABI” is very useful. You might think it would be clearer if we called it “the platform ABI,” and in fact that is often done! This might lead you to believe that we could dispense with all the *programming language* parts of C—like type declarations and header files and `.o` objects—and just focus on the *binary interface* bits, which really don’t need to be considered through the lens of C at all.

Sadly, if you actually try to implement a compiler, you will rapidly discover that this ABI is painfully difficult to disentangle from the programming language C. In order to make a C call, you don’t just need to know what registers to pass arguments in and what size an `int` is, you need to know things like struct layout and what it means to spill a struct onto the stack. You also need some way to take the interface specifications provided by operating systems and turn them into a format your compiler can work with, and wouldn’t you know it, those interface specifications are provided in the form of… C header files.

No, really: go down this rabbit hole and it will start to drive you mad. Pick just about any syscall that involves a struct type and try to figure out how to automatically determine how to generate the right code to use it. Consider the case of `stat`. The man page says it’s in `sys/stat.h`, but how do we even find where that header is on some arbitrary system? [The highest-voted answer to a Stack Overflow question about how to do this on Linux is to call `gcc`.](https://stackoverflow.com/q/13079650) I assure you it does not get any easier to avoid giving in and calling a C compiler from here.

The reality is that C header files are currently used as the lowest-level [interface description language](https://en.wikipedia.org/wiki/Interface_description_language) you can get your hands on, and this is a somewhat frustrating choice seeing as it is virtually impossible to parse them without writing a C compiler. But let’s suppose you did that—you deduced all the platform-specific rules for layout and calling conventions, you reimplemented the C preprocessor, and you wrote a fully-featured C parser and symbol resolver that allows you to parse any standard C header file—you will rapidly run into *another* problem.

The header files on your system are not written in standard C.

## C ABIs are difficult to separate from C toolchains

This part can come as a bit of a shock to people, especially since there’s some idea that the header files found on your computer are compiler-agnostic. In fact, if you go digging through your system’s header files, you’ll find an awful lot of `#ifdef __GNU_C__` and `#ifdef __clang_major__` littered about. In practice, your header files are very much vendor-specific, they just explicitly handle a small list of known vendors.

At this point it may become clear how hopeless this all is. The deeper you look, the fuzzier the line between your operating system and the C compiler becomes. In a way, the entire C toolchain is a part of the operating system, the wildly complex decoder ring you are forced to use if you want to be able to conform to the eye-wateringly elaborate machinations required to call into your operating system. Yes, I realize this might sound hysterical, but consider just how deeply the C toolchain is interwoven with languages that, frankly, would rather have as little to do with it as possible:

* Rust compiles to LLVM, and [rust-bindgen](https://github.com/rust-lang/rust-bindgen/) uses libclang.
* GHC tries its best to implement a common subset of the platform calling convention for its FFI, but users found this insufficient enough that [it now supports a mode that just generates a `.c` source file containing a stub function and compiles it with a C compiler](https://downloads.haskell.org/ghc/9.8.1/docs/users_guide/exts/ffi.html#the-capi-calling-convention).
* The Swift compiler straight-up [includes an embedded libclang](https://llvm.org/devmtg/2014-10/Slides/Skip%20the%20FFI.pdf).

There will always be people who quixotically struggle to avoid depending on the C toolchain at all costs, but doing so is essentially hopeless. Even if one were to implement everything required to consume system header files without MSVC, GCC, or Clang, support for those headers is such a moving target that it would likely create a headache-inducing maintenance burden. What’s more, C’s exceptional ubiquity only entrenches it further: when software written in different languages needs to coexist in the same process, the mediator is the collection of languages, tools, runtime infrastructure, and operating system support that we now know under the moniker “C”. Why not something else? Because everything already supports C. So everything has to support C.

To quote the [current Twitter bio](https://twitter.com/__phantomderp) of JeanHeyd Meneide, current Project Editor for the C standardization committee:

> The C Standard Cannot Be Replaced And Will Never Be Destroyed.

Quite.
> **Comments:**
> **username_10** (2023-11-26): (re. "C does not specify an ABI, nor is it really defined at the level that would allow specifying one"), C (the standard library) does pretty much define the storage format for strings, though, and that does end up as part of the ABI for any OS calls that expect e.g. a filename from the caller.  
> **username_11** (2023-11-26): "There will always be people who quixotically struggle to avoid depending on the C toolchain at all costs, but doing so is essentially hopeless." I've got some projects where this was most expedient. Recent cases involved tools to repair fundamental damage to the C userspace infrastructure, so *not using it* was essential to getting the job done. Yes, this meant looking up the syscall ABI and invoking it directly.  
> **username_0** (2023-11-26): ...also, I got a little lost in the sentence "...a compiled language that uses its own binary object format and does its own linking and loading and has its own runtime can largely get away with not knowing how to generate code that calls into C code because a C call can always yield to the runtime’s scheduler, which is written in a language that knows how to perform the call into C" -- could you either expand on that, or maybe bring that description one step closer to lay terms? I didn't quite understand "yield[ing] to the runtime's scheduler" and what entity makes the call into C thereof...?  
> **username_9** (2023-11-26): @StoneThrow I think that getting into that is largely out of scope for this answer, but essentially: if you have a runtime, then that runtime can always be used to assist with making calls. For example, if the language is garbage collected (using a stop-the-world collector), there fundamentally must be some way for the program to temporarily stop executing and allow the GC to do its work. This is known as “yielding to the runtime”, and that same mechanism can be used to request that the runtime make a call on the program’s behalf.  
> **username_0** (2023-11-26): @AlexisKing -- I apologize for the extended chat, I know it's frowned upon, but this Q&A has revealed many enticing rabbit holes to explore. My question was perhaps a bit more primitive than you might have interpreted: in this context, does the word "runtime" mean "stuff the language provides during the program's time of execution that was not explicitly written by the coder"? E.g. garbage-collection, from, maybe, languages like Go...the coder doesn't explicitly garbage-collect, but the language provides it -- is that, then, (part of) the language's "runtime"?  
> **username_9** (2023-11-26): @StoneThrow Essentially: yes.  
> **username_12** (2023-11-26): Funny you mention macOS because that was the huge exception. Classic macOS was not C-based, but Pascal. The Carbon API made it more C-friendly, but most functions utilized Pascal calling, as it was needed for Macintosh Toolbox, and Pascal strings were everywhere. Carbon was finally removed in macOS 10.15 (2020).  
> **username_13** (2023-11-27): Old Windows was also more Pascal based than C based -- this shows up in calling conventions with `cdecl` vs `stdcall`.  C mostly came into the picture with the dominance of UNIX.  
> **username_0** (2023-11-27): Is it fair for me to summarize that (nearly) all programming languages' runtimes ultimately have a reliance on libc to make system calls, because libc has already implemented all the ugly/difficult/low-level work of making those system calls, e.g. which registers are used to pass values? I gather from other commenters that there are "equivalents" to libc, written in other languages like Pascal, so presumably programming language implementers could choose to "call out" to the Pascal-equivalent-of-libc if they had a foreign-function interface to Pascal?  
> **username_14** (2023-11-27): @StoneThrow: Yes, *on UNIX* and similar systems (such as non-Android Linux). But no, on Windows and Android. Windows is especially interesting because its `COM` API's are thinly disguised C++ API's.  
> **username_15** (2023-11-27): To add to the list of OSes: OpenBSD is considering restricting syscalls to libc (its own, obviously) for security reasons. The security aspect, as I understand it, being that it's easier to craft a syscall than it is to craft a call to a function from an ASLR loaded libc, and therefore mandating going through libc will make exploits more difficult to perform.  
> **username_16** (2023-11-27): @StoneThrow Historically yes. In modern times no though. You can’t get away from the calling conventions of the OS kernel as long as you’re using an OS, and pretty much all of them use C, C++, or some close equivalent these days. So, for example, if you were to try to use Free Pascal’s runtime libraries in place of libc for your tools, you’d still be making C ABI calls to the OS itself, if not calling into the platform’s libc routines.  
> **username_17** (2023-11-28): On Windows, Microsoft now maintains portable API descriptions (winmd) for Win32, COM and WinRT. The official Rust bindings for Windows are generated from these.  
> **username_0** (2023-11-28): @AustinHemmelgarn - I wonder if I might be able to nudge you to expand your comment on the historical/modern aspect into an answer. I know extended chats are discouraged, but my curiosity is piqued by your statement that "if you were to try to use Free Pascal’s runtime libraries in place of libc for your tools, you’d still be making C ABI calls to the OS itself, if not calling into the platform’s libc routines" -- I don't fully understand why: I thought the point of Pascal's runtime libraries would be to be outright equivalent to libc, i.e. offer their own syscall implementation, etc.  
> **username_6** (2023-11-28): Re: "C ABI" even though ISO C doesn't define an ABI:  The way to read that term is that it's an ABI that supports C, making compatible C implementations possible.  A C ABI defines all the functionality needed by C, but usually not much more.  e.g. it defines how to call functions with a fixed signature or C-style variadic, and to return one value, possibly an aggregate.  And sizes / alignments for the C primitive types. You can have multiple ABIs on a platform, like AArch64 64-bit vs. ILP32. Or some embedded platforms like ARM Linux have had a few different ABIs, not binary compatible.  
> **username_6** (2023-11-28): A C ABI doesn't have to define anything only C++ needs, like conventions for vtable layout.  A C++ ABI does that on top of a C ABI; for example `g++` uses what's called the "Itanium C++ ABI" across most (all?) platforms as its convention for name-mangling, vtables, how try/catch exception blocks find each other, and when by-value args can actually be passed by value in registers vs. when they actually get passed by pointer in terms of the C ABI so the object always has an address. (something like non-trivially-copyable)  
> **username_16** (2023-11-28): @StoneThrow Historically, there were systems that provided interfaces other than the C ABI as the primary or only ABI. Prominent examples include Lisp machines from various vendors, WD’s Pascal MicroEngine, and the Java Card platform. But the C ABI is what UNIX and Windows systems provide these days, so that’s what software that runs on those platforms has to use. Even if you use assembly code to make a system call on Linux, you’re still using C calling conventions and the C ABI to make the call, _because that’s the only way you can make the call_.  
> **username_16** (2023-11-28): @StoneThrow And even aside from the OS kernel ABI, there are quite a few things that libc makes significantly easier to work with. For example, on Linux, system call numbers vary based on what CPU architecture you’re on (as an example, `read` is 0 on 64-bit x86, 3 on 32-bit ARM, and 63 on 64-bit ARM). libc hides all of that cleanly though, so you don’t need to care as long as you’re only calling things it supports. A lot of other languages take advantage of that (among other things) when running on Linux, even if they are not inherently tied to C.  
> **username_8** (2023-12-03): "On Linux, one can perhaps quibble with this (though I still think it would be quite ill-advised)" For those who would enjoy quibbling with this, [I have written somewhat at length](https://www.matheusmoreira.com/articles/linux-system-calls) about it on my website. Linux system calls are stable and can be safely used directly. "it is the intended mechanism by which every programming language in existence calls into your operating system" Not _every_ language. I created a lisp interpreter with builtin syscall support. Go also does it. There are more examples but I'm running out of characters :)  
---
### Answer by username_3 on 2023-11-25. Score: 22
(note: this answer is addressing a quite different question from the original revision: *why one language would have a dependency on another language?*)

> Is there is something fundamental about C that other languages like Rust simply cannot do?

Yes. C is the language of the Linux kernel. `libc` is a fundamental library for interacting with the kernel, and reimplementing it in a non-C language would be a bit of a fool's errand (you'd still be interfacing with C regardless). Of course, [you don't have to use libc to use Rust](https://users.rust-lang.org/t/clarifications-on-rusts-relationship-to-libc/56767) for ex. embedded targets, but if you want your code to compile and run and do useful things on Linux, you're going to have to link against it. And to link against it (or anything on Linux), you'll [need a C compiler](https://github.com/rust-lang/rust/issues/39915#issuecomment-618726211), and have to [talk with the C ABI](https://faultlore.com/blah/c-isnt-a-language/) (see [Alexis King's answer](https://langdev.stackexchange.com/a/3237/2303) for much more in depth about *this*).

> Or is it that existing C code is so well written that there's no point reinventing those wheels in newer languages?

Sometimes. [`curl`](https://github.com/curl/curl) is an excellent and battle-tested library despite being written in C. I've seen a number of languages - off the top of my head, [some libraries in Nim](https://github.com/treeform/puppy#about) - prefer to link to curl rather than implement the layers of the networking stack themselves. Rust libraries may do the same, though I do not know for sure.

> Does it have anything to do with modern programming languages abstracting away certain primitive programming concepts like pointers/memory, thereby leaving some things "impossible" in the newer language?

No. There are certainly things that are impossible to do in some programming languages: an obvious example is nontermination in languages that are total, like [Agda](https://en.wikipedia.org/wiki/Agda_(programming_language)#Termination_checking) (excluding bugs). If you have certain restrictions on how you want to accomplish a task - for example, if you want to return a reference in a language that disallows it like Lobster, or if you want to have a binary under X bytes in a language known for bigger binaries like Rust, then that certainly can be "impossible", but quite a lot of programming languages that abstract over raw concepts like pointers and memory have some sort of escape hatch to allow you to do what you could do in an unsafe language.

> I've heard, anecdotally, that some Python packages rely on C code.

A lot of Python packages rely on C code for performance: for example, [`numpy`](https://github.com/numpy/numpy). This is because Python is Really Goddamn Slow and less so due to *limitations* of Python.

> So perhaps my question is a more generic "Why does language X have any dependency on C code? Why isn't it language X 'all the way down'?"

There are likely more answers to that that I'm unaware of, but you laid out the questions for the answers I know of nicely in your own post.

---

(I welcome improvements to this answer. I'm not so confident about my knowledge of `libc`, in particular.)
> **Comments:**
> **username_0** (2023-11-25): When you say "`libc` is a fundamental library for interacting with the kernel" do you mean functions like `open()` etc.? Where do language authors draw the line between "what's worth reinventing" and what's not? Is it that essentially, _C came early enough that it implemented the first kernel-interacting library,  i.e. `libc`, which became time-tested and battle-hardened over time, therefore it would be a "fool's errand" for newer languages to reinvent specifically this kernel-interacting library?_  
> **username_1** (2023-11-25): @apropos You don’t need a C compiler to link against libc, though, so the first paragraph doesn’t actually give an answer. That’s especially the case for Rust which can innately produce C-compatible exports and imports itself.  
> **username_1** (2023-11-25): @StoneThrow POSIX defines all system interactions in terms of the C interface and does not define any other kernel interaction, so libc-kernel is one thing as far as userland is concerned. Things like [`open`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html), which fundamentally interact with the system, need to be accessed that way (or by platform- and even version-specific non-standard interfaces, like numbered syscalls; Go does try to do that). The need to move in lockstep means there are very few even replacement libcs for any platform.  
> **username_3** (2023-11-26): @MichaelHomer The point is you need *libc* to link against libc. C is a special language because libc is written in C, and libc is essentially part of the kernel interface. You certainly don't need a C compiler to link against it though, yeah, but I *think* (not sure here) that's because the C ABI is special (by virtue of being the de facto ABI).  
> **username_1** (2023-11-26): Yes, you do need libc to link against libc, but that isn't part of the question; the Linux kernel is one of the few things *not* to be mentioned in it! It's a gesture at an answer, but without actually being an answer.  
> **username_3** (2023-11-26): Their question is why a language would have a dependency on another language, and one of their nicely broken down sub-questions is "is there something special about C". C is special by virtue of being the language of the Linux kernel.  
> **username_1** (2023-11-26): Since the question was reopened I have edited it down to be a single question rather than closing it as "Needs more focus"; you may want to update accordingly.  
> **username_1** (2023-11-26): "C is special by virtue of being the language of the Linux kernel"; but what does Linux have to do with this programming language's packages? This is an argument the answer could make, but it doesn't, and it doesn't *obviously* connect to this need for a C compiler - it needs to explain why these are things that the Rust user needs to care about.  
> **username_3** (2023-11-26): Linux has to do with the programming language because Linux is a Big Target for compiled code. If you want your program to run on Linux, and you don't link against `libc`, you can't do much of value. I'll update my answer to clarify.  
> **username_3** (2023-11-26): Also @MichaelHomer - your edit makes this a *very* different question than originally posed, I think it should be reverted. The question used Rust as an example but was very much not exclusive to Rust, it was asking what the reasons there are for a language to rely upon another language. There are a fairly small number of answers to that, which is why I think this question is in scope, and I think I hit on them all in my response - C is special, other code can be good, the host language can be slow.  
> **username_1** (2023-11-26): You can revert it, though the question certainly needed more focus - the mere fact that you're answering five disconnected questions illustrates that! If it can be edited into a focused question that preserves more of what you want, great. The author indicated that they were aiming at why "every layer isn't rewritten for every language", but also said the question was specific to Rust; I'm not sure how the most general version of that couldn't be unanswerably broad. This is one risk of speeding to answer a question you see is being closed, unfortunately... It can be edited further though.  
> **username_7** (2023-11-26): It's somewhat a misnomer to say you need the C ABI to interact with the kernel. You need the implement the syscall API. Sure types/structs need the same ABI, but no function calls. Would be a lot of work (which as far as I understand is why rust doesn't do it), but wouldn't require calling into C (unless you count the kernel's syscall handler, but those have unique calling conv anyways). Also you didn't mention it here, but rust also uses libc for dynamic loading/_start/address space setup.  
> **username_0** (2023-11-26): @apropos - I'm still digesting your answer as well as AlexisKing's answer, and all the surrounding comments. Forgive me if I somehow misunderstood or overlooked this in your existing answer, but the impression I've gotten is that (certain) Rust packages will potentially need to _link to `libc`_, i.e. they have a dependency on a _library_ written in C, as opposed to "C _code_" as was stated in "The Rust Programming Language" -- is that correct? I.e. were the book authors (presumably) glossing over the distinction between C _code_ and a _library written in C_?  
> **username_0** (2023-11-26): @apropos - your "you don't have to use libc to use Rust" link seems to indicate that Rust's need for a C _compiler_ is merely to invoke the linker (presumably ld) -- did I understand that correctly? If so, why use a C compiler to invoke ld? Why not just invoke ld directly?  
> **username_3** (2023-11-26): @StoneThrow I believe that's correct. ld/lld is interwoven with a C compiler toolchain. I dug up [some more details here](https://github.com/rust-lang/rust/issues/39915#issuecomment-618726211).  
> **username_8** (2023-12-03): "you'd still be interfacing with C regardless" -- this is not true, on Linux it's possible to interface directly with the kernel via system calls, the ABI is stable, AOT and JIT compilers could generate code following that calling convention. "if you want your code to compile and run and do useful things on Linux, you're going to have to link against it" this is also not true, I'm working on a freestanding programming language right now, a standalone lisp interpreter written in C with zero dependencies, it's a completely static executable that doesn't link to anything including libc.  
---
