Does Rust have a runtime

Perhaps daft question, but I've came across opinions and perhaps articles (I don't remember now) stating that Rust doesn't have any runtime. I've also heard that this isn't true and in fact Rust has runtime.
What's the story with that?

The story is that "does language X have a runtime" is too vague a question to be answered precisely.

What Rust doesn't have is a garbage collector, an interpreter, or anything that adds continuous or unavoidable overhead while your code is running. Some people call this a runtime.

What Rust does have is support functions that the compiler can emit calls to when necessary, and interfaces to the operating system that even C needs to have (e.g., I/O). Other people call these "a runtime".

Depending on the definition on "runtime", you can make either assertion true or false.

27 Likes

Rust technically has a very lightweight runtime. When people say Rust has no runtime, they actually mean Rust has same runtime to C's runtime, instead of some heavy runtime like JVM or Python interpreter.

If you categorize C as a no-runtime language, then Rust has no runtime as well.

It's really just depends on how you define "runtime".

Personal rant: IMHO, it's much more suitable to categorize Rust has no runtime. When people ask "Does Rust have a runtime", they almost never mean "Does Rust support syscall or Does Rust has pre main lifetime".

I get that it feels good to say "Actually...", but it's really just creating more confusion, or straight up misleading, if the wording is not clear enough.

11 Likes

A runtime I understand to be some environment without which the software cannot run. C has runtime. Same for C++.

1 Like

And there are also some contexts where "runtime" actually means "async runtime", like Go. Rust doesn't have this built-in either, one has to use something like tokio or async-std explicitly.

7 Likes

When you build the firmware for some microcontroller, what kind of "environment" does this software (firmware is software, after all) need? I guess there's none, it works directly with the bare metal. And Rust (like C) can be used for this task, so it can be used without runtime.

7 Likes

Makes perfect sense. This is it. Thanks.

But do note that on such constrained environments, many parts of std are not available. You can't usually perform memory allocation, I/O, or anything that normally requires the assistance of an OS (with any language). So the question, when properly asked, is more nuanced than that.

Of course, at the end of the day, Rust is not magic. It's just another high-level language that you can implement however you please. The current major implementation, rustc, follows the traditional "ahead-of-time compilation to machine code" model. This is not really inherent to the language, and there could be other implementations that do have a "runtime" such as a GC or an interpreter.

3 Likes

No, C and C++ do not have run-times. It is perfectly possible to compile a C/C++ program into a binary, load it into an empty machine (bare metal) somewhere where the processor will find it at reset, and away you go. No other code than what you wrote in your C/C++ program will be present. This is after all how the Linux kernel and other operating systems work. This is how millions of micro-controllers run their application code.

What C and C++ have is a standard for useful library functions that a compiler implementation is expected to provide. There is nothing special about that, they are just functions that you can implement yourself if you need them in your application. Something I have done for embedded systems.

There is the issue of setting up the stack pointer, initialising globals, etc, etc that needs to be done before your C code is run. Typically some assembler code with a name like "crt" something. That is "C run time". A misnomer in my mind as that tiny bit of code is not actually used once your C program is running.

C and C++ only need a processor to run them. I am not about to refer to the hardware as C's run time.

1 Like

GC and interpreter definitely need runtime

I did not assert the opposite.

Whist it is typically true that much of the standard library may not be available on small systems in C one can implement as much as one needs oneself. It is perfectly possible to do I/O directly from C (reading and writing peripheral registers) It is perfectly possible to incorporate memory allocation into ones application, just write a malloc() and free().

Anyway, I like to think that Rust is capable of all that C can do.

You are right that Rust could be implemented with an interpreter, or some VM byte code like Java, indeed we have WASM as a target, however it seems to me that many languages cannot be implemented as ahead of time compilation. Dynamic languages like Javascript. For those there is no getting away from requiring a run time.

I hate to be the "well actually" guy (do I actually?), but:

What you say is true for C, the only "runtime" it has is some glue between how the OS (or UEFI, BIOS, or even CPU itself) initializes the system state and what main expects. The Linux kernel too has some early (platform specific) ASM code to set things up in a sane state.

There may also be some code after main returns, typically to handle turning the return value from main into a exit syscall (bare metal code doesn't return so not applicable).

You could also argue about a few functions in the standard library that are really more of compiler builtins because they do weird things that can't be implemented in the language itself (alloca, va_list and a few things like that come to mind).

Rust is pretty similar, though it adds another small layer on top of crt when it comes to normal hosted implementations (may not be the case for no-std, crt can be skipped then). It has a handful of functionality that has to be "compiler magic" (lang items in case of Rust).

Now with C++ (and Rust) things are a bit different. Why? Exceptions/Unwinding. Arguably the code to handle exception/panic unwinding is part of this same micro runtime for both C++ and Rust. And at least parts of it can't be written in the language itself as I understand it. And this is more of a real runtime as it takes control away from the program.

I argue that runtime is not a yes/no thing, but exists on a spectrum from "not at all" (assembly), via "hardly any" (C, Rust and C++, probably some other traditional compiled languages (Pascal or Ada anyone?)), all the way to "definitely" (most languages?).

3 Likes

Well now you're opening up the can of worms of what's C and what's "C as provided by gcc version foo with flags bar etc", and "the specific subset of C we figured out we can use even without a bunch of runtime-ey stuff" since Linux and these microcontrollers you mention generally can't use most stuff from the C standard library, and often even have parts of the language they can't use too.

Sure, but that's why C exists in the first place, isn't it? It was created to port Unix from PDP-11 assemble to high-level language.

It would be a bit strange to say that in a discussion of some language we shouldn't talk about it's raison d'être, about something which gave us said language in the first place.

And that is the can of worms of if K&R C is C :sweat_smile:

C does have a runtime library. Rust also has a runtime shareable library brought in through the prelude code. That is how the default i/o files get set-up. It also provides the unwinding code for diagnostic purposes. Do a rustc --print sysroot . Look at the library directories under the sysroot.

Of course you can write rust that doesn't use the default prelude. That is probably the first step in writing embedded code. Note that a helloworld program in C is much smaller than in Rust because of all of the code brought in with the prelude.

As for code that runs in the background, neither rust nor C do that, unless you code it up.

An interesting exercise is to attempt to write the minimum helloworld. In C you do it by suppressing the default c runtime library, omitting the library initialization (c0rt), and using the write system call (from section 2 of the manual. I've managed to get the entire image under 20 or 30 bytes. I am beginner at rust so I haven't tried suppressing the default prelude, but rust has syscalls crate.

Nope, don't believe that. Not if it is ELF at least. The file header is larger than that! Raw code running on embedded that outputs over UART? Sure, I could see that.

See Tiny ELF Files: Revisited in 2021 which was written in assembly and put code into unused bytes of the file headers. The record there is 105 bytes.

1 Like