Are/were they any attempts to have Rust's std free of c dependency, something like zig?
I’ve seen some interesting examples, including:
Is there hope that the Rust standard library will be entirely free of any dependency on libc in the future?
Are/were they any attempts to have Rust's std free of c dependency, something like zig?
I’ve seen some interesting examples, including:
Is there hope that the Rust standard library will be entirely free of any dependency on libc in the future?
I don't understand the point. this is just not what rust is aiming for.
for one, rust doesn't have a runtime, it is not that kind of (sometimes referred to as "batteries-included") language that provides "everything" you need to write your (cross-platform) application, like java, python, etc. rust brands itself as a "system" language, and it is intended to be seaminglessly interopable with the operating system and native libraries through ffi.
although people love to "rewrite" FOOBAR in rust, the rust ecosystem consistes of large portion of "bindings", and practically, any non-trivial application would inevitably have dependencies on some native libraries, so your app end up linking against libc
anyway, what's the point of "getting rid of" libc
for the rust part?
for another, the language itself doesn't need libc
at all, you can even implement your own libc
in rust (although some features needs nightly rust for now, notably, varadic functions). it's the std
crate that has a dependency on libc
, we all know rust can support many target architecture platforms, though many of them are #![no_std]
only.
removing the libc
dependency from std
just means we need to re-implement the features of libc
in std
, it's not just re-inventing the wheels, it's also throwing away already existing good wheels for no benefits whatsoever.
what's more, libc
is the syscall "stub" library for posix systems. linux is the only exception to have a stable syscall ABI because the kernel and userland are maintained by separate groups of people. all other systems, posix-like or not, have no stable syscall ABI guarantees at all, the only supported way to call the kernel is through some userland stub library, such as ntdll.dll
for Windows, libSystem.dylib
for Macos, and for posix systems, it's just libc.so
--- it's just linux also provides stable syscall guarentee in addition to libc
s (plural because there exist different libc
implementations).
even the "redox" operating system, which is written in rust, has a libc
, called relibc
, and in what language is it written? you guessed it, rust!
the convention to call the system interfacing library libc
is for historical reasons: it originated from unix, since C
is originally just for unix. the C lauguage is eventually adapted by everyone and standardized, and in the standard, the supporting library is called the (C) standard library, not referenced as libc
officially. but informally, people would usually use the term "C standard library", "C runtime library", and "libc", interchangably. though depending on context, "libc" may mean different things.
also, for posix systems, the C standard library is typically provided through libc
, or in other words, libc
can be seen as a superset of the C standard library on such systems.
also, there's some relevant discussion in this recent thread regarding syscall stability and libc.
Then it is probably the only language that just has a manual page about “Runtime”:
You may have heard the claim that Rust doesn’t have a runtime. While that’s true at a high level—it doesn’t have a garbage collector, an interpreter, or a built-in user-level scheduler—it’s not really true in the strictest sense. Specifically, Rust does have some special code that runs before your main function and in response to certain special conditions in your code, which really is a form of bare-bones runtime. (from Rust for Rustaceans, page 215
Yeah, we can quibble about the definition of "run-time". And I will..
Sure there is code that gets run before main(). Setting up the stack and initialising static data etc. C, C++ and many others have that too. And it may have bits of code tied to panics or whatever. I'm not prepared to call that a run time in the sense used in Javascript, Python, Java and other such languages. None of it is being run as the code you wrote runs. Ergo not "run time". Correct me if I'm wrong here.
I think you are looking for this issue:
I would love for this to be implemented, but the issue is almost 7 years old and there is disappointingly little work on it. Amusingly (in a bad way), even the green-field WASI target depends on libc by default (in a MUSL-like fashion) and there is no alternative to it.
Show me yours, I’ll show you mine (Jill Lepore on arguments)
Facts:
Every programming language specifies an execution model, and many implement at least part of that model in a runtime system. One possible definition of runtime system behavior, among others, is "any behavior not directly attributable to the program itself". This definition includes putting parameters onto the stack before function calls, parallel execution of related behaviors, and disk I/O.
By this definition, essentially every language has a runtime system, including compiled languages, interpreted languages, and embedded domain-specific languages. Even API-invoked standalone execution models, such as Pthreads(POSIX threads), have a runtime system that implements the execution model's behavior.
From Runtime System
Not really. Just one persons definition of what turns out to be a very fuzzy idea of "run time".
For example I think it's absurd to say that the way a compiler arranges to push parameters onto the stack before function calls is a "run-time". I can do the exact same thing in assembler. Where is the run time then? Just because I get a compiler to write that for me does not introduce a run time.
But sticking to what is stated in that definition, where is the "behaviour of my Rust code that is not directly attributable to the program itself" ?
Sure there is behaviour in libraries that does not appear in my source. Sure there is behaviour in the OS sys calls I make that does not appear in my source.
They are not in the Rust language itself. There is no run time in Rust.
It would be nice to have it as a MUSL alternative on Linux.
However, many other operating systems don't have such a direct stable kernel API, and don't allow bypassing libc
or its equivalent (MSVCRT, libSystem, etc.).
Go tried not to use libc at all, and had ran into compatibility issues (1, 2).
I think you meant NTDLL.DLL. It's a library that's providing Win32 API to applications and, notably, it doesn't include libc, it's literally a thin wrapper around syscalls, not too much different from Linux's vDSO – except in Linux vDSO is optional extra-efficient way to access syscalls while in Windows it's the only way.
What's your goal from this? How does std not depending on c make a material difference to you?
Your TCB includes C anyway. It's not practical to get rid of that.
Directly calling ntdll ("Windows Native API") is not supported or (officially) documented, though. Windows reserves, and has previously used, the right to change the native API.
Since the Linux syscall API is officially documented and supported, the actual comparison would be the publicly documented APIs like kernel32, user32, etc...; the Windows libc equivalents msvcrt and ucrt are implemented on top of these, and indeed Rust does, to my knowledge, directly call to these.
However, as @kornel has said, there's lots of platforms (to some extent including Linux!), where libc is the only actually supported public API, as frustrating as that is.
There's actually quite a few reasons you might not want to use libc, primarily for me the large amount of global in process state that it drags in and applies to seemingly random calls, sometimes differently per platform, sometimes without documentation, mostly without thread safety. At least if you're writing against some specific os syscall you can generally trust some other thread isn't calling setenv() via some other API and toasting you.
Hopefully as some of these platforms notice that there's been another language invented since 1970 they might clean this up, but for now this is still C's world, we're just living in it.
And yet, even Redox, an OS written in Rust, has libc as its stable API/ABI. (It would be nice if it took the opportunity to make a no-global-state API the stable API, and put a libc emulation layer on top instead.)
It can't avoid it entirely, as it aims for POSIX compatibility.[1] But it would be nice to make the libc part of POSIX in particular an emulation layer on top in cases where there is a saner API (errno, I'm looking at you).
If I made a clean slate OS I would probably be looking at Midori for inspiration instead. Async first and async everything would be pretty neat. I wouldn't be doing managed code like Midori, so you would still end up with process barriers though. But I think building an OS on a queue similar to io-uring as the core primitive would be interesting. Almost no blocking syscalls. Fuschia would also be interesting as a source of inspiration. ↩︎
Who told you that? Sure, normally you want to call something like OpenFile from kernel32.dll, to be portable and support Windows 9X and Windows CE, but NtOpenFile is also perfectly documented and stable.
The fact that your program wouldn't support Windows 9X or Windows CE is probably not a big deal in a modern world with both OSes being well and truly dead.
There's a small handful of APIs (nowhere near enough to use as a platform API) that have effectively "leaked out" into the public API for various reasons, yes. Those are fine to use, technically, though they are generally identical to the actual public API and don't have a link library so there's really no legitimate reason to use them, especially since you already have to use kernel32 to even load it in.