I've just read that rust doesn't have a runtime:
"Contrary to what you might be used to with other languages, Rust doesn’t have a built-in runtime. "
What does it mean? How is it possible?
I've just read that rust doesn't have a runtime:
"Contrary to what you might be used to with other languages, Rust doesn’t have a built-in runtime. "
What does it mean? How is it possible?
Mostly, this means that Rust doesn't have garbage collection.
Since this blog post is about async Rust, I'd think they mean that Rust has no async runtime.
That means that in other languages, like Go, there is an async runtime included by default, while in Rust, you need to choose your own. A popular async runtime is for example tokio.
But C++ also doesn't have a garbage collection and yet it does have a runtime. I really don't understand it. When I think about it, I really don't understand what the runtime is. What is it?
I think they mean what they say: The rust doesn't have runtime. Otherwise they would specify it I think. I may be wrong though.
Simply, a runtime is a program that runs in parallel to your code and does some stuff related to your code. Java, Go, Python - all these languages have a runtime, while Rust doesn’t (there’s a thing called rt, but it’s basically not a runtime).
C++ doesn't have a runtime, at least not in the sense used here.
Every single mention of “runtime” in this article is talking about “async runtime”s, and IMO it’s quite obvious.
What is not so familiar is that you need to pick a runtime to actually run your asynchronous code.
An async application should pull in at least two crates from Rusts ecosystem:
We’ll use
tokio
for the purpose of this tutorial. You should get to know at least one runtime and focus on that first. You can check out other runtimes later.
Runtimes
Contrary to what you might be used to with other languages, Rust doesn’t have a built-in runtime. We won’t discuss the pros and cons of that here, but you’ll need to make a choice and pull that in as a dependency.
Some libraries require you to use a specific runtime because they rely on runtime internals to provide a solid API for you to use or wrap their own API around an existing runtime. One example is the
actix_web
web framework, which wraps its own API aroundtokio
.Most of the time, you can choose any runtime you want. But no matter which runtime you choose, there are three basic operations you should figure out how to do before you start coding:
- How to start the runtime
- How to spawn a
Future
- How to spawn blocking or CPU-intensive tasks
1. Starting the runtime
You can explicitly instantiate the runtime and spawn a future onto it.
let mut rt = tokio::runtime::Runtime::new().unwrap();
2. Spawning a
Future
on the runtime
… and so on …
Of course for a lot of other “higher level” programming languages, the language’s runtime includes code for non-blocking, i.e. async, IO and/or green threads, and/or things like that; so “async runtime” is not a separate concept there. But in Rust, an async runtime is its own thing, as you can see particularly well once it explicitly appears in the code, e.g. as the tokio:runtime::Runtime::new()
in the code quoted above.
Besides the fact, that this article is only concerned with “runtime”s in relation to async
code, the more general statement “Rust doesn’t have a runtime” without any context that this is about any kind of “runtime”-like features in particular (like about async
in this article) is also commonly claimed to be true; and IMO it is true. However the precise determination whether or not the general statement “Rust doesn’t have a runtime” is true depends on the exact definition of what you do or do not consider a “runtime”, and there are purists out there that would consider some functionality that Rust does provide, such as unwinding, or stuff happening before and after fn main
, an (albeit very small) “runtime”.
Except the use of it when it mentions just runtime. But I agree that they do mean async runtime. They are just not precise about it.
That is the "difficulty" in having same name for different or slightly different things. Of course C++ has a runtime, but it is not async runtime, although now with coroutines being part of C++ even that statement isn't true anymore.
C++ runtime includes all machinery to actually start one's app init statics etc.
In that meaning Rust also has a runtime. I believe it is exactly the runtime used by C/C++.
Would that be correct?
Thing is languages like C, C++, Pascal, PL/M, Coral, Ada, Modula, ... and Rust compile your source code into actual machine instructions, in binary, that can then be run by your processor with no other help. You don't need a run time to run them. You don't even need an operating system. Which is why they get used to write operating systems.
Contrast to languages like Javascript, Java, Python, PHP, etc that are run by an interpreter of some kind. There are all shades of "interpreter" from being a program that reads the source code and immediately does what ever it says, like some old BASIC interpreters or shell scripting languages. To things like Java that compile down to some ind of binary executable but the instructions in that executable are not those of your actual machine, they need to be further interpreted. Like the Java Virtual Machine.
So in that respect no, C++... Rust do not have a run time.
Of course there is generally a little bit of code that sets up a stack and heap memory and intializes some variables required before actually running your compiled Rust code. In the C world this is known as the "C Run Time" (cut), but none of that is actually running after your compiled C/Rust is started so that is nothing like a Java or Python "run time".
Enter the world of async. Here we have asynchronous tasks. They give the impression of doing many things at the same time. Hence they need some kind of support to switch from one async task to another. Confusingly this is also known as a run-time. But it's different to what I have described above, it
s the "async run-time". Indeed in Rust that async scheduling is done by code in whatever async library/crate you are using. So arguably still there is no run-time in the Rust language itself.
Contrast to Go, which supports some kind of thread/task idea and the scheduling of those is built into the language, in it's run-time.
Confused yet? Not to worry, often terms usied in computing have multiple meanings. Or are very flexible in their usage.
Hi, The link you've provided is for the rather high level API for a "runtime", which under the hood uses good old fashioned libc like memcpy etc.
I might quibble with this, actually, in both C and Rust. However, I'd only do so in the context that a runtime is the collection of code and tooling that implements the language-specific abstract machine on top of the physical machine and operating system. This isn't the sense OP's article means - they use "runtime" to mean tools for scheduling asynchronous tasks, but it is a widespread use of the term "runtime" in language design, and related to OP's question that way.
In a C program, the portions of the C runtime that translate between the operating system's process setup and calling main
stick around until main
returns, or until the process is aborted and unloaded. For multithreaded programs, the CRT's process-management code often runs per-thread, both before thread startup and after the user program running on that thread terminates. This is necessary because there's work to do to bridge between the termination semantics of the program as defined by the language, and the termination semantics of processes as constrained by the OS and hardware: setting the exit status, for example.
It's not "running" in the sense of evaluating instructions on an ongoing basis, but it's there, just as much as a function that's up-stack from the current activation is still present and still in some sense active. It'll kick in again when main returns, just like any other function waiting for a callee to return.
Rust has a "runtime" in this sense, which is quiescent while the program is running, as well. For example, the Rust runtime handles converting panics to a sensible exit status and generating the error message, and translating main
return values into process exit statuses. It's not a ton of code, but it's not none code, either, and it's code provided by the implementation to give user programs the environment Rust promises.
The C (and Rust, and Go, and…) runtime also includes all the code that translates the language's IO primitives into operating-system-specific and hardware-specific code, and numerous other on-demand services used to abstract away parts of the real environment for the programmer's benefit.
Yep, That is exactly what I've suspected. Thanks!
I think C/Rust runtime can be safely described as:
Environment/Ecosystem that allows application to start, interact, and close that application.
Yes - but I agree with @steffahn that the way that article is using the term means "async runtime," which means something different and a bit more constrained.
I also agree with him btw
I find this statement self-contradictory. If the thing is not running whilst the code of my C program is running then how can it be a run-time? It is not running at run time.
Now I know they refer to the start up/shutdown code as a run-time in the C world. Can't argue with that. But it can be as small as only a dozen instructions that runs from a processor reset, sets up stack, clears some memory and then jumps to main(). When main exits it can be as simple as a single HALT instruction. I have done this kind of thing on embedded systems. I don't think the C language standard requires anything more.
As far as I know the same is true of Rust. After all the compiled binary is pretty much indistinguishable from what you would get compiling C code. And people run Rust n micro-controllers with only those handful of support instructions I describe above.
This is very different from the notion of a Java or Go run-time. Which is why we don't see Java or Go being used on micro-controllers. And these are very different from Rust's async run-time.
Maybe this will help: "Rust Before main" Rust Before Main - Ryan Levick - Rust Linz, July 2022 - YouTube
This is getting nitpicky and arguing what a runtime should be called will go nowhere.
Java has an entire virtual machine and Go does not. These don't really fit in the same bucket together.
But we do! JVM was once hailed as the next big thing and several attempts were made to standardize (and minimize) it enough to run Java on microcontrollers. Embedded Go and emgo exist today. Even MicroPython is a thing. And Espruino (JavaScript), too! These are all "slimmed down" subsets of the respective languages, and arguably they all require a "runtime", but here we are.