Rust in 2023

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:

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:

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:

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:

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:

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 structs and enums and tuples, and gradually expand it to more complex constructs, like traits, references, borrow checking, and unsafe.

If we both:

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:

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.