Rust 2020: Finish what we started and a bit about proc macros

Its that time of year again and its been exciting watching the language grow over this past year! Looking back through the Rust releases for the entire year, starting with 1.32.0 there’s been some fantastic progress, which is pretty easy to forget! So why not start with a bit of a recap of some of the big things from each release:

1.32.0 (2019-01-17)

  • ? as a macro-repetition operator
  • The literal macro_rules! fragment specifier
  • Self as a constructor and pattern for tuple and unit structs, along with being allowed in type definitions
  • Stabilization of all of the numeric to_{be,le}_bytes and from_{be,le}_bytes conversions
  • The dbg! macro was stabilized

1.33.0 (2019-02-28)

  • Multiple patterns in if let and while lets, along with warn-by-default irrefutable patterns
  • Importing items (especially traits!) as _
  • More non-Self method receiver types: Rc, Arc, Pin
  • Quite a few methods/functions made const
  • Pin API stabilized!

1.34.0 (2019-04-11)

  • Atomic integer types stabilized
  • Try{From,Into} stabilized
  • Alternative registries stabilized in cargo

1.35.0 (2019-05-23)

  • Box<dyn Fn{Mut,Once}> now implements the respective trait
  • The contains method on range types stabilized

1.36.0 (2019-07-04)

  • Non-Lexical Lifetimes enabled on the 2015 edition
  • std’s HashMap implementation replaced with hashbrown::HashMap which gives a pretty significant performance improvement with no user code change necessary!
  • alloc crate stabilized
  • MaybeUninit stabilized
  • Future trait stabilized along with the task module

1.37.0 (2019-08-15)

  • _ can now be used as an identifier for constants
  • Profile-Guided Optimizations flag made available
  • Trait objects without dyn prefix now warn-by-default

1.38.0 (2019-09-26)

  • Pipelined compilation enabled!
  • Pointer type cast methods stabilized
  • Some undefined behavior lints around mem::{uninitialized, zeroed}

And probably one of the most anticipated releases to date:

1.39.0 (2019-11-07)

  • async/.await stabilized, allowing for ergonomic asynchronous code on stable!
  • Shared references allowed in bind-by-move pattern guards stabilized!

Wow, what a year! There’s only one release left in this calendar year, but it’ll be tough to beat the hype around 1.39 ;)

A lot of other things happened over the past year that didn’t quite get the attention that some of the things that made the release notes did, namely const_generics has made quite the progress! What used to ICE the compiler very frequently now doesn’t do it quite as often, and there’s some great ergonomics being made already with them, such as trait implementations for all array sizes (even if they are still gated by LengthAtMost32, and remember, never call my_array.into_iter() as it is right now folks!)

So I think Rust 2020 should be a similar story to Rust 2019, and other people seem to agree: make great progress on the work we’ve started and ship some awesome new features that make the language even better to use.

But, I’d also like to take some time to mention something that doesn’t quite get enough love in my opinion: the ergonomics around proc macros. Proc macros play a huge role in the Rust ecosystem today, the biggest probably being derive proc macros. They help reduce boilerplate and can make some rough edges around the language a bit more bearable. I think that last part is particularly important – if something that’s usually very tedious or sensitive to do by hand, having a proc macro available to do it is a massive boon! Though, as you may know, proc macros aren’t all sunshine and rainbows themselves. There’s some pretty major pain points around them:

  1. Proc macros require their own crate.

    This is a huge ergonomic issue and it makes creating new proc macros a nuisance at best.

  2. No parsing library that ships with the language

    It feels like a bit of a missed opportunity to me to not have included a parsing library for users of the language to use out of the box – instead proc macros have to deal with the 3 Crates of the Compile Time Apocalypse™: syn, quote, proc-macro2 (okay maybe that’s a little harsh, but syn takes a significant amount of time to compile!)

    Don’t get me wrong, there’s nothing inherently bad about relying on a 3rd party crate to parse a language, but it ends up feeling like a chore to set up a proc macro because of this sometimes.

  3. Compile times

    To run off of my above sentiment, proc macros can significantly increase the compile time of projects, mainly because of the dependency all of them have on syn. It also turns out that proc macros are not pipelined at all with their dependencies as it stands right now.

  4. proc_macro_hygiene

    The proc_macro_hygiene feature is the last(!) feature that is preventing rocket from hitting stable Rust now in its current form. Other libraries definitely would make use of this as well, as can be seen from the over 1 million all-time downloads on the proc-macro-hack crate. Its a major missing piece of the proc macro puzzle, in my opinion, and it would be great to see some progress on it. Though as of the time of writing, the pull request that enables Fn-like and attribute proc macros to generate macro_rules! items was merged 26 days ago.

I’m sure there’s more issues that will come up and/or that I’m not aware of, but those are definitely some of the biggest problems proc macros face today, in my opinion, and moving the ergonomics of them forward would at least help smooth off some edges with the language as they stand today.

Thanks for reading and happy soon-to-be 2020 :)