# Elementary default runtime

**URL:** https://internals.rust-lang.org/t/elementary-default-runtime/18423
**Category:** libs
**Created:** [February 26, 2023, 3:01pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423 "2023-02-26T15:01:58Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![userxfce](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/userxfce/32/10360_2.png) [@userxfce](https://internals.rust-lang.org/u/userxfce)
#### Post date: [February 26, 2023, 3:01pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/1 "2023-02-26T15:01:59Z")

</div>

References: [https://rust-lang.github.io/wg-async/vision/unresolved\_questions/default\_runtime.html](https://rust-lang.github.io/wg-async/vision/unresolved_questions/default_runtime.html)[https://rust-lang.github.io/wg-async/vision/shiny\_future/users\_manual.html](https://rust-lang.github.io/wg-async/vision/shiny_future/users_manual.html)

> [@Should the standard library have a basic Future runtime?](https://internals.rust-lang.org/t/should-the-standard-library-have-a-basic-future-runtime/10705):
>
> For discovery and user-friendliness reasons, should std include a basic, maybe multithreaded, future runtime? It seems strange to have to include a separate crate to even run a Future. (As a separate note, that same argument applies to future combinators, which I personally believe should either be on the standardized Future trait, or at least in the standard library, but that's a separate discussion.) [poll](https://internals.rust-lang.org/t/should-the-standard-library-have-a-basic-future-runtime/10705/1)

[https://www.reddit.com/r/rust/comments/i4759z/where\_are\_we\_heading\_regarding\_runtimes/](https://www.reddit.com/r/rust/comments/i4759z/where_are_we_heading_regarding_runtimes/)

It is really bad when a valid program in a language doesn't compile (let alone run) without (un-/standard) dependencies. Then, not being able to introduce async/await without "yes, it does not even compile: please add this before main(), please add this to the .toml, I will talk about these later" is not encouraging.

Proposal is to pick:

1. simply define "async" to nothing and "await" to actually-execute-the-function-here, basically turning async/await into "defer" (aka the .Net strategy); or
2. get inspired by JS and follow the event loop

The other point is the runtime incompatibility. I/Os appear to be the main culprit there. Adding them to the std?

---

<div class="post-metadata">

### Author: ![jjpe](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jjpe/32/9779_2.png) [@jjpe](https://internals.rust-lang.org/u/jjpe)
#### Post date: [February 26, 2023, 4:20pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/2 "2023-02-26T16:20:16Z")

</div>

> [@userxfce](#):
>
> It is really bad when a valid program in a language doesn't compile (let alone run) without (un-/standard) dependencies.

TBH, while it may appear a bit odd at first, it's never really been a problem for me. When I'm using async I know about it and its consequences, and to assume the same holds true in general wouldn't be outlandish.

---

<div class="post-metadata">

### Author: ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)
#### Post date: [February 26, 2023, 5:17pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/3 "2023-02-26T17:17:44Z")

</div>

I think it would make sense to include a _minimal_ async runtime (basically [`pollster::block_on`](https://docs.rs/pollster/latest/pollster/)) in `std`, because there is more or less only one way to write that, and it solves the problem of "I need to run some generic code that is designed to permit async components and is therefore async itself, but I am not doing anything really interesting with the async".

Multithreaded async, and async IO runtimes, are _much_ bigger design spaces and therefore much less good candidates for `std`.

---

<div class="post-metadata">

### Author: ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)
#### Post date: [February 26, 2023, 6:14pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/4 "2023-02-26T18:14:30Z")

</div>

`block_on` was attempted to be added to `std` a few years ago

> <https://github.com/rust-lang/rust/pull/65875>
>
> As disussed on Zulip within wg-async-foundations:
> 
> This change adds \`std::thre…ad::block\_on\_future\`, which represents a minimal Futures executor.
> It is modelled after futures-rs \`futures::executor::block\_on\`, which blocks the current thread
> until the Future had been driven to completion.
> 
> The implementation is a bit more efficient than the futures-rs one, since it doesn't require an
> additional allocation and some refcount manipulations. It also does not require TLS, since it
> makes use already existing TLS information about the current Threads state in the standard
> library.
> 
> Feature-wise the main difference to the futures-rs implementation is that lacks the reentrancy
> detection that is implemented in the futures-rs version. If someone calls \`block\_on\` from
> within an async fn there which already runs inside a \`block\_on\` executor it will panic. This one
> will not, and just spawn another executor. Depending on the code that runs in those executors
> this can either work out fine, or lead to a deadlock issue, since the wrong executor handled the
> remote \`wake()\` signal. However this isn't deemed as an issue, since wrong usage of APIs can
> already lead to deadlocks in other places. Added the deadlock detection back in would require
> another TLS variable.
> 
> Naming-wise I called this now \`block\_on\_future\`, but it's totally open for discussion. Just calling
> it \`block\_on\` gives a bit too less information on what it's about. I first had \`blcok\_on\_task\` - but
> in the end most users won't know what a task is, and instead only observe it being passed a
> \`Future\` as parameter. Something along \`\[a\]wait\_future()\` might also work, but then maybe it's
> mistaken with the \`await\` mechanism, which does something different.

Conclusion from that was "needs RFC", (because it turns out there's _not_ just one way to write it). There's a wg-async team issue about writing such an RFC, but no movement on it that I can see

> <https://github.com/rust-lang/wg-async/issues/11>
>
> We saw https://github.com/rust-lang/rust/pull/65875 closed and requiring an RFC.… The working group should probably pick this up.

---

<div class="post-metadata">

### Author: ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)
#### Post date: [February 26, 2023, 6:37pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/5 "2023-02-26T18:37:06Z")

</div>

Yes, we should absolutely have one. We're going to need to finish the async traits in the standard library first, though.

---

<div class="post-metadata">

### Author: ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)
#### Post date: [February 26, 2023, 7:38pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/6 "2023-02-26T19:38:42Z")

</div>

Do you mean `async fn` in traits, or adding specific traits? If so, which ones?

---

<div class="post-metadata">

### Author: ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)
#### Post date: [February 26, 2023, 7:55pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/7 "2023-02-26T19:55:05Z")

</div>

Among other things, I think before we add a runtime to the standard library, we're going to need a `trait AsyncRuntime` or similar. That's leaving aside the async read/write/etc traits.

---

<div class="post-metadata">

### Author: ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)
#### Post date: [February 26, 2023, 8:02pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/8 "2023-02-26T20:02:36Z")

</div>

While async traits are certainly helpful in designing a `block_on` for std, I think they're far from necessary. Imho `block_on` _should_ be the minimal single-threaded executor\[1\]. Even if we get a standard interface `spawn`, I don't think `block_on` should interact with it in any way. Even if std provides a default executor with a thread pool. (And for that reason, putting it on the trait is too aggressive; it should be a free function in the `task` module imho.)

* * *

1. I am _far_ from fully aware of the options here, but roughly: poll the future, park the thread if it's pending, and the waker unparks the thread. The use of a condvar/futex/etc beyond just thread notification is a potential implementation choice, but a) not particularly relevant to the interface, and b) scoped threads currently just use standard thread parking despite already necessarily having an atomic count it could futex/notify/wait on.

---

<div class="post-metadata">

### Author: ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)
#### Post date: [February 26, 2023, 8:10pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/9 "2023-02-26T20:10:34Z")

</div>

Personally I think _both_ should be free functions, and then we can add a mechanism for changing the backend they use via a trait or traits.

---

<div class="post-metadata">

### Author: ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)
#### Post date: [February 27, 2023, 12:39am UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/10 "2023-02-27T00:39:56Z")

</div>

Agree on both being free functions, but if global\[1\] `spawn` is pluggable (it should be imho), `block_on` really doesn't. If we have context-relative `spawn`, it could matter, but if we don't and downstream futures use the global `spawn`, it basically doesn't matter which loop is polling the main driver future. For the case where you really do want it on the pooled executor, you can always `block_on(spawn(task))`.

Having a guaranteed-simple-single-threaded `block_on` is I think a benefit even when a potentially-pooled `spawn` is available\[2\]. In a way, it's similar to the "`now_or_never`" "executor," in that its existence is to turn nominally-`async` code into nominally-sync code in the trivial manner (i.e. use the same underlying syscalls, etc. but userland block waiting for them rather than yield the thread to another task).

* * *

1. I'm a believer in passing more context down on the waker/context, although that'll require new API to enable and for that context not to be lost when wrapping context/waker. (Give us the `Provide` API for carrying arbitrary executor/etc info please!) 

2. Under such a model, a simple pooled executor can just be `for _ in 0..n { thread_scope.spawn(|| loop { block_on(tasks.steal()?)?; }); }`. I don't have any idea how achievable it is, but I like the vision of the future where the reactor driving future progress is itself another future on the executor.

---

<div class="post-metadata">

### Author: ![withoutboats](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/withoutboats/32/4560_2.png) [@withoutboats](https://internals.rust-lang.org/u/withoutboats)
#### Post date: [February 27, 2023, 1:01am UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/11 "2023-02-27T01:01:03Z")

</div>

(NOT A CONTRIBUTION)

Block on does not need to be tied to "global executor" stuff or anything like that, and probably shouldn't be. There should definitely be a simple single threaded non-concurrent executor in std, with std-defined behavior, that doesn't depend on choosing a runtime. There is no reason this hasn't happened except lack of drive.

---

<div class="post-metadata">

### Author: ![Yoric](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/yoric/32/1316_2.png) [@Yoric](https://internals.rust-lang.org/u/Yoric)
#### Post date: [February 27, 2023, 9:46am UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/12 "2023-02-27T09:46:50Z")

</div>

In the OCaml world, we were faced with similar difficulties and we ended up with a "OCaml Batteries Included" distribution, which was meant to be easier to start with for beginners. I've left the OCaml world a few years ago after a burnout, so I'm not sure how things are going these days, but there was lots of enthusiasm at the time for this.

Actually, come to think of it, I think that the fact that OCaml now has a working package manager has probably decreased the need for a Batteries Included distro.

---

<div class="post-metadata">

### Author: ![simonbuchan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/simonbuchan/32/9390_2.png) [@simonbuchan](https://internals.rust-lang.org/u/simonbuchan)
#### Post date: [February 27, 2023, 11:51pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/13 "2023-02-27T23:51:58Z")

</div>

I've long said I'd be happier with a much thinner std, given how good traits and cargo are. You don't _need_ fs, most of sync, most of the collections, just about all intrinsic methods...

This isn't a call to remove things, but it's such a low bar to add things to your dependencies that I don't know what the real benefit would be, urge than perhaps discoverability. The special place std has is knowledge and lockstep updates with the compiler, and being able to escape the orphan rules due to being at the bottom of the dependancy stack, and being a shared vocabulary crate is a nice effect too.

Discoverablity is also pretty easily addressed, by warnings recommending crates, too.

---

<div class="post-metadata">

### Author: ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://internals.rust-lang.org/u/2e71828)
#### Post date: [February 28, 2023, 6:02am UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/14 "2023-02-28T06:02:43Z")

</div>

> [@simonbuchan](#):
>
> The special place std has is knowledge and lockstep updates with the compiler, and being able to escape the orphan rules due to being at the bottom of the dependancy stack, and being a shared vocabulary crate is a nice effect too.

There’s also provenance: I trust the compiler developers and code review process enough to let their software run on my computers, but that trust doesn’t necessarily extend to third-party crates— Even those that are written by people who are also Rust compiler developers. In practice, adding additional dependencies to the project can have significant soft costs beyond the mechanics of `cargo add`, such as evaluating the implementation quality (either by developer reputation or code review), staying on top of updates/security issues, etc.

---

<div class="post-metadata">

### Author: ![simonbuchan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/simonbuchan/32/9390_2.png) [@simonbuchan](https://internals.rust-lang.org/u/simonbuchan)
#### Post date: [February 28, 2023, 12:19pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/15 "2023-02-28T12:19:39Z")

</div>

I think that's a cultural issue far more than a technical issue. We trust std more than say, rustcrypto crates because... why exactly?

Deno decided to not bless their standard library, and there's no provenance issue there.

---

<div class="post-metadata">

### Author: ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://internals.rust-lang.org/u/2e71828)
#### Post date: [February 28, 2023, 12:40pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/16 "2023-02-28T12:40:06Z")

</div>

> [@simonbuchan](#):
>
> We trust std more than say, rustcrypto crates because... why exactly?

Speaking only for myself, I trust `std` _because_ it's developed in lockstep with the compiler by essentially the same team and processes. I had to investigate the quality-control processes used for this project before trusting the compiler, and that investigation necessarily covered `std` as well. I haven't yet done that for `rustcrypto`, and so distrust it by default— I have no first-hand evidence either way.

Mentally, I have 3 trust levels for code I didn't write, and it takes a significant investment of my time to elevate something from one trust level to the next:

- Level 0: Untrusted. Worth consulting for ideas, but not allowed to run on my computers.
- Level 1: Trusted implementation. I have done at least a cursory code review or other quality assessment, and am willing to run the specific version I've looked at. Most third-party crates I use fall into this category, and get pinned to the version I looked at.
- Level 2: Trusted process. In addition to looking at the quality of a specific version, I've determined that the project governance can be trusted to maintain that quality and keep out malicious code. This is the category that `rustc` and `std` fall into.

If parts of `std` were split out into separate projects, those new projects would be downgraded to trust level 1 until I got a feel for how the new governance structure operates in practice.

---

<div class="post-metadata">

### Author: ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)
#### Post date: [February 28, 2023, 12:54pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/17 "2023-02-28T12:54:47Z")

</div>

IMO one of the problems with saying stuff should be added to `std` because it's more trusted is that one of the main reasons it can be that trusted is that it is small. Tokio, just the main repo not including dependencies developed by the same project but in other repos, is approximately the same size as `std` (65kSLoC for `tokio` vs 79kSLoC for `std`). If larger projects such as this get merged into `std` that would necessitate increasing the maintenance team (which is already [not able to keep up](https://internals.rust-lang.org/t/did-we-start-reviewing-prs-slower/18033/2)) diluting the trust inherent in having a small set of known developers.

---

<div class="post-metadata">

### Author: ![simonbuchan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/simonbuchan/32/9390_2.png) [@simonbuchan](https://internals.rust-lang.org/u/simonbuchan)
#### Post date: [February 28, 2023, 1:16pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/18 "2023-02-28T13:16:58Z")

</div>

I don't disagree, but there's nothing special about std there, nor would it involve more work for you to review it to get it to your level 2 were it hosted on [crates.io](http://crates.io) (for example). I mentioned rustcrypto as a project specifically because their governance and procedure is as if not more strict than std (necessarily), to demonstrate there's only a cultural or even only a psychological difference to provenance.

But I don't want to derail this thread too much: the main point is I feel that encouragement for new users to use (popular, well reviewed) crates is a good thing for rust, in general, especially those coming from C or C++ backgrounds where using a library is a huge pain in the butt.

---

<div class="post-metadata">

### Author: ![userxfce](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/userxfce/32/10360_2.png) [@userxfce](https://internals.rust-lang.org/u/userxfce)
#### Post date: [February 28, 2023, 5:44pm UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/19 "2023-02-28T17:44:12Z")

</div>

First things first: below Rust only opcodes shall remain. Even ASM (&DSL) shall find its place in Rust. Rust has to stick to bare metal, at all costs. This is paramount.

The proposal (v1) is _not_ about a minimal runtime, but precisely about a _zero-cost_ runtime.

A _zero-cost_ runtime is in fact a compiler abstraction, which in this case projects a multi-threaded async/await program onto a single-threaded async/await execution system. For analogy, think std::unique\_ptr.

This introduces no dependencies into Rust programs, just in the compiler (that is, one more module to write). It is what .Net did when got async/await in C(from F)#, and IIUC it is the same thing that @withoutboats & @kpreid propose (it is also the right idea).

It is a proposal intended to plug the first gap in the async/await Rust eco-system. De facto, there are two Rust versions now, out of which one only compiles on systems able to multi-thread. This is an already existing dependency in the language and should be avoided at all costs.

The second gap to be plugged is exactly the incompatible divergence between the async runtimes themselves (e.g. tokio vs. async-std vs...), which shall not be allowed to go beyond test-and-prove concepts. Each of these runtimes already creates its own eco-system (and on top of that each runtime kinda rewrites the entire std to its own image). A _standardized_ mechanism of compatibility between these eco-systems/runtimes is necessary.

The gap(s) may appear small at this time, but there are precedents: D programming language knew the war between the two roses named "phobos" and "tango", which were competing for the status of the-one-true-standard-library, which plagued the project beyond immagination. It took D2, almost a new language, to stop that, and even that only because the "tango" team gave up. Similar fates are to be avoided in a (solid) Rusty world.

One more thing: the as of yet most under-appreciated quality of Rust is _not_ its safety, but its explainability: it codes an intent _unambiguously_ (parenthesis: the post about the alternative proposal of using unique/multiple references instead of mutable/immutable references saw that correctly towards its last paragraphs, and in a way acknowledged that mutable/immutable is the better choice in the end [1](https://www.reddit.com/r/rust/comments/2581s5/informal_survey_which_is_clearer_mutability_or/)). Coding the intent is paramount for the future, when codebases will be gigantesque and all the (intelligent?) tools that will analyze (and, for sure, write) that code will rely essentially on this markup of the intent. It is this quality that makes Rust the first (and maybe the single-ever-needed) universal programming language (UPL): able to cover everything from the bare metal to humongous systems _in an explainable way_. C did not make it there not because of its lack of expressiveness, but precisely because of its lack of unambiguous intent/explainability. Ditto, C++. Other languages did not touch the bare metal, so "why bother?".

As of now, the areas still restricted to Rust remain the browser (because wasm is still purposefully restricted) and the scripting (getting a bit Rustic at times...). As for universality, the mutable value semantics of Val are the strongest competing idea, a paradigm that Rust itself might (have to) integrate at a future moment.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [March 3, 2023, 1:14am UTC](https://internals.rust-lang.org/t/elementary-default-runtime/18423/20 "2023-03-03T01:14:54Z")

</div>

The distrust of non-std crates ends up mixing concerns:

"I need to trust security and quality of the code I use" → "there should be an API, possibly with shared global state, spawning numerous threads, bundled with nearly every Rust program".

I understand the difficulty with vetting [crates.io](http://crates.io) code, but bundling everything into std instead creates lots of side effects.

- `std` has very limited ability to evolve APIs due to being forever 1.x version,
- `std` implementations are basic or one-size-fits-all, which isn't always desirable, especially for something complex like an async runtime which affects performance and stability of servers.
- `std` is bundled with most programs, increasing executable sizes. LTO is slow and doesn't always eliminate everything. When dependencies actually use the `std` runtime, users of other runtimes will end up with two runtimes.
- `std` comes precompiled and doesn't respect all of the build settings.
- `std` affects portability of Rust, and already not all of it makes sense in environments like browser WASM.

Currently lack of a built-in runtime encourages libraries to return futures instead of spawning them themselves. This is actually good for ability to control how the futures are executed, prioritizing/deferring their execution, ensuring prompt cancellation, etc.

If there's a need for an official Rust async runtime, this could be done without it being in `std`. The Rust org could release or bless some async runtime crate. [crates.io](http://crates.io) could have special display for "rust official" crates. Maybe even the Rust installation could come with a bundled crate (like the `test` crate exists on nightly). All this can be done without making `std` heavier and undermining applications' control over async execution.

[Next page](https://internals.rust-lang.org/t/elementary-default-runtime/18423.md?page=2)
