Reasonable Performance

Rust 2022

October 20, 2021

In the last few years, at the end of the year, the Rust Development Team asks people to write blog entries about their wishes for the next year. The new year is still a bit off and no call for such posts has yet been made in 2021, but there are a few things that I have in mind and that I want to write about right now. So without further ados, here is my wish list for Rust in 2022

Compile times

I wrote about compile times at the end of 2018 and in 2021 I want to point at that blog post and say: “that”. It’s depressing how little things have improved — from the perspective of a user — when it comes to compile times. I work with a wonderful team on a product in Rust, but though our repository is not even 40,000 lines of code, it takes such a long time to build. It’s roughly 4 minutes for a debug build from scratch and 10 minutes for a release build from scratch; incremental builds in debug take about 90 seconds and it’s about 3 and a half minutes in release mode. This is an absolute killer of productivity. Every time that I make a small change — even just adding a dbg!() statement in a function — I have to sit and wait for Rust to finish re-building and re-linking the project.

Some may argue that a lot of progress has been made since 2018 and will point to charts that show the compile times of various Rust projects going down month after month, year after year. And although they are technically right, it doesn’t matter. I’m still sitting in front of my computer, waiting minutes for a build, getting bored, going on lobste.rs, and losing my flow and concentration. The compile times of Rust are improving, but they’re not even within the ballpark of being acceptable.

Another thing that some people say is to control dependencies. I agree whole-heartedly with this point: bringing in large dependencies in order to use only a tiny fraction of their functionality is not usually a good trade. (I recently stopped using a library to do ISO-8859-1 to UTF-8 conversion when I discovered that it took only two lines of code to do it by hand. Five dependencies gone, just like that!) Unfortunately, some of the most common and useful crates in the Rust ecosystem are big and slow to build. For instance, we use tokio, serde, and hyper in some of our projects; they add minutes to the build times, but what’s the alternative? We have our own expertise and async runtimes and web servers are not it.

I hope that Rust build times improve in 2022. We need them to improve drastically. Not a by a few percent, but by an order of magnitude.

Debugging

Since compiling takes so long, doing exploration of a code base by sprinkling in dbg!() statements is not a great experience. I tried using rust-gdb to follow code paths and inspect data, and although it works well enough for simple programs, it’s not without its issues.

The first problem is probably not super complex to tackle (famous last words): Strings within structs are not pretty-printed. Compare below the output of printing a variable of type String and a variable which is a struct containing a String:

(gdb) p a
$1 = "Hello, world!"

(gdb) p b
$2 = dbg_demo::MyStruct {inner_string: alloc::string::String {vec: alloc::vec::Vec<u8, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {ptr: core::ptr::unique::Unique<u8> {pointer: 0x55555559baa0, _marker: core::marker::PhantomData<u8>}, cap: 13, alloc: alloc::alloc::Global}, len: 13}}}

The same happens with other common types: vectors, sets, maps, etc. It would be very nice if printing and navigating a struct in the debugger did not expose the internals of those objects (unless explicitly required I suppose).

The other thing that should be worked on is async support. With rust-gdb, if you set a breakpoint on an async function, it’ll immediately return. In a sense, it’s understandable: the function doesn’t do any work per se; it creates and returns a Future that will do the work, but from a user-experience perspective, not being able to debug async functions the same way we debug regular functions is very problematic.

Normally, I wouldn’t care too much about the state of debug support (I’ve never been quite good at using debuggers), but given that compile times are such as they are, we should be able to extract as much value from one compile as possible. Improved debug support would go a long way toward that.

More restraint in the implementation of features

This one is more of a “get off my lawn you darn kids” rant from a programmer who’s gaining more grey hair each month, so take with a grain of salt. Programming languages cannot be everything for everyone and the job of language designers is to be judicious in the choice of features they include and even more judicious in the choice of features they omit. This could be a reddit-tinted view of the situation, but it appears to me that a number of features are coming down the pipe that will make the language more complex, harder to learn, harder to master (and worst of all, harder to convince other people not to use those features) that address very niche cases.

If I were 5–10 years younger, I’d probably be very excited at those new advanced language features; as an increasingly grumpy and crusty coder, those new features scare me, because I’m afraid (1) that I won’t be able to properly learn them (I still haven’t fully grokked Pin, GATs are probably going to be something I want to avoid, and I’m really hoping the push to make Rust Haskell with type families and GADTs will not come to be); (2) that other programmers will start using those features just because they can and not because they are necessary to the solution to the problems they are solving.