The Rust programming language is pretty neat. I use it for my personal projects, like Millet, a language server for Standard ML. I’ve also made a few contributions to official Rust projects.
So, the spirit of various other posts, here are the main developments I’d like to see for the Rust programming language going forward into 2023.
Fleshing out existing features
We recently had Rust 1.65, which brought us the MVP of let else
and generic associated types (GATs). These features, GATs especially, have been in the works for a while. And although there are still things to work out with them, like:
rustfmt
support forlet else
- better diagnostics and fewer restrictions on GATs
It was great to see these long-awaited features make it to stable.
We can continue this effort by expanding support for existing, partially-done features. Here are some existing MVP-level features and restrictions on them that would be great to address.
const
generics
The MVP of const
generics allows const
parameters on items, but only if the parameters have certain “integral” types, aka:
- The signed and unsigned integer types (
u8
,i16
, etc) - Characters (
char
) - Booleans (
bool
)
Furthermore, there are restrictions on what operations can be done on the constant parameters. So for instance, the type of a function that adds an element to a fixed-length array, which would look like:
fn push<T, const N: usize>(xs: [T; N], x: T) -> [T; N + 1]
is not allowed, because of the N + 1
in the return type.
const fn
There has been a slow march of allowing more and more Rust language constructs to be used in const fn
. For instance, while
, loop
, and if
are now allowed. This has in turn allowed more and more functions in the standard library to be const
.
However, many Rust features remain disallowed in const fn
, like calls to trait methods. This also means for
loops don’t work, since their desugaring involves calling IntoIterator::into_iter
and Iterator::next
, which are trait methods.
A big blocker for supporting trait methods is the fact that it’s not yet been decided how trait definitions will support const fn
.
impl Trait
Existential impl Trait
is currently only allowed in one place: the return type of a regular function. It’s not, for instance, allowed as:
- The return type of a trait method
- The right-hand side of a type alias
async
and await
You can’t currently have async
in traits. async-trait
, the de facto workaround, has millions of downloads, but incurs a slight performance hit in the form of extra heap allocations.
This is a hard problem to solve at the language level. However, we are making progress, particularly on other features that are ultimately required for async
in traits, like GATs and impl Trait
.
!
, the never type
Currently !
is semi-stable, in that you can write it as a return type for functions, but not in other places.
Related to !
is another feature: exhaustive patterns. This would allow the compiler to know that in this example:
let Ok(x): Result<_, !> = foo();
the Ok(x)
is actually an irrefutable, exhaustive pattern, because the Err(_)
case is impossible, because the error type is !
.
Improving infrastructure
In a similar vein to finishing incomplete language features, there are some long-running efforts to refactor the underlying infrastructure of rustc
. These include:
- Using chalk for trait solving
- Using polonius for borrow checking
- Improving incremental compilation and the query system
Being conservative about new features
Although we should flesh out existing, partially completed features, outright new features make less sense at this point in Rust’s evolution.
Rust is already quite a complex language. The marginal benefit of adding a new feature is often outweighed by the costs of adding that feature, like:
- Making sure the feature interacts correctly with every existing feature
- Implementing, documenting and testing the feature
- Teaching the feature to newcomers
This attitude of conservatism is, actually, applicable to fleshing out existing features as well. We should really make sure that expanding the scope of existing features doesn’t open up soundness holes.
Specifying the language
Speaking of soundness holes, we should commit to a machine-checked specification of Rust with operational semantics and other formal methods.
There are already efforts in this area:
But these efforts do not have an official commitment from the Rust project. We should develop a machine-checked specification that the community agrees to be the definition of Rust. This would allow us to know with full confidence that the language itself is well-formed.
Such a specification would need to be developed incrementally. We could start with defining simpler constructs, like struct
s and enum
s and tuples, and gradually expand it to more complex constructs, like trait
s, references, borrow checking, and unsafe
.
If we both:
- Make steady progress in formally specifying the language
- Slow down the addition of new language features
We could eventually get to the point of having the entire Rust language be fully specified and checked for soundness.
This would be an monumental achievement for computer science and software engineering. Rust would be the most widely-used programming language for which we have a full, proven specification.
Indeed, most modern PLs are either:
- Very popular but not formally specified, like Python
- Formally specified but less popular, like Standard ML
Even languages like C, C++, and JavaScript, which have a specification and are also widely used, are only specified with English-language prose. This means they lack a mathematical soundness proof.
Having a machine-checked definition would also cement Rust’s status as delivering safety and reliability.