One of Rust's defining features is the ability to write functions which are generic over their input types. That allows us to write a function once, leaving it up to the compiler to generate the right implementations for us.
When we introduce a new keyword for something which used to be a trait, we not only gain new functionality - we also lose the ability to be generic over that keyword. This proposal seeks to change that by introducing keyword generics: the ability to be generic over specific keywords.
This proposal is scoped to the const
and async
keywords only, but is designed
to be leveraged by other keywords as well in the future. Keywords are valuable,
generics are valuable, users of Rust shouldn't have to choose between the two.
We're in the process of adding new features to Rust. The Const WG is creating an extension to Rust which enables arbitrary computation at compile time. While the Async WG is in the process of adding capabilities for asynchronous computation. We've noticed that both these efforts have a lot in common, and may in fact require similar solutions. This document describes a framework for thinking about these language features, describes their individual needs, and makes the case that we should be considering a generalized language design for "keywords" (aka "definitely not effects"), so that we can ensure that the Rust language and standard library remain consistent in the face of extensions.
const fn
and async fn
are similar language extensions, but the way they
extend the language is different:
const fn
creates a subset of "base Rust", enabling functions to be executed during compilation.const
functions can be executed in "base" contexts, while the other way around isn't possible.async fn
creates a superset of "base Rust", enabling functions to be executed asynchronously.async
types cannot be executed in "base" contexts 1, but "base" inasync
contexts is possible.
+---------------------------+
| +-----------------------+ | Compute values:
| | +-------------------+ | | - types
| | | | | | - numbers
| | | const Rust |-------{ - functions
| | | | | | - control flow
Access to the host: | | +-------------------+ | | - traits (planned)
- networking | | | | - containers (planned)
- filesystem }--------| "base" Rust | |
- threads | | | |
- system time | +-----------------------+ |
| | Control over execution:
| async Rust |---{ - ad-hoc concurrency
| | - ad-hoc cancellation
+---------------------------+ - ad-hoc pausing/resumption
In terms of standard library these relationships also mirror each other. "Base"
Rust will want to do everything during runtime what const
rust can do, but in
addition to that also things like network and filesystem IO. Async Rust will in
turn want to do everything "base" Rust can do, but in addition to that will also
want to introduce methods for ad-hoc concurrency, cancellation, and execution
control. It will also want to do things which are blocking in "base" Rust as
non-blocking in async Rust.
And it doesn't stop with const
and async
Rust; it's not hard to imagine that
other annotations for "can this panic", "can this return an error", "can this
yield values" may want to exist as well. All of which would present extensions
to the "base" Rust language, which would need to be introduced in a way which
keeps it feeling like a single language - instead of several disjoint languages
in a trenchcoat.
Role | Github |
---|---|
Owner | Yosh Wuyts |
Owner | Oli Scherer |
Liaison | Niko Matsakis? |
Footnotes
-
In order to bridge async and non-async Rust, functionality such as
thread::block_on
orasync fn
must be used, which runs a future to completion from a synchronous context.const
Rust does not require such a bridge, since the difference in contexts is "compile time" and "run-time". ↩