Enforcing style in CI for Rust Projects

A Google image search for ‘stylish dog’ says: “Arguing about style is silly, just *be* stylish (by using tools to enforce code style).”

It’s a common trope that developers love to argue about code style. From the age old JavaScript debate over semi-colons, to even older debates over “tabs vs spaces”, there’s been a lot of emotional energy spent on this topic.

This article isn’t a new addition to that debate. Instead, this article will attempt to help you avoid the debate entirely, at least in your Rust projects, by explaining how you can use the rustfmt tool to enforce style guidelines using CI. We’ll start with a brief introduction to rustfmt, so feel free to skip the next section if you are already familiar.


Hello rustfmt!

rustfmt is a tool for Rust projects that helps enforce style. It runs a set of lints against your code and will fail if any of the checks don’t pass.

To use rustfmt you first must add the component, via rustup:

rustup component add rustfmt-preview

Then, to run it:

cargo fmt

It’s worth noting that when you run cargo fmt it will change your code formatting. It will only fail if it cannot safely change your code. It’s quite smart though, and generally can take care of most of the problems for you, leaving just a few for you to solve on your own.

You can configure the default lints if you would like. For more on that, read this. Generally speaking, I don’t recommend configuring a tool like this, but I know that some folks really like to.


The rest of this article will discuss using rustfmt in CI. We’ll use Travis CI for our examples, but you can use whichever CI provider you’d like without any significant edits.


Stable or Nightly

When testing on CI, one of the first decisions you need to make is which versions of Rust to test on. You’ll need to pick which channel is best to test your code on- and this is likely enforced by the dependencies your project uses.

Now that rustfmt is in the mix, there’s a few more things to consider. As it currently stands, rustfmt is slightly different on stable vs. nightly channels. If you are able to pick just one channel, the examples below should be very useful for you. We’ll discuss how to handle projects with multi-channel support in the next section.

Stable: use write-mode diff

Default setup for testing both code and style on the stable channel. https://gist.github.com/ashleygwilliams/faac3850441ff5f98d54cea02a54ec78

On stable, your .travis.yml should use--write-mode diff. This feature will be deprecated in favor of --check, which is currently on beta and will land in the next release in early August (~1 month).

Nightly: use check

Default CI setup for testing code and style on the nightly channel. https://gist.github.com/ashleygwilliams/30c9bf3b39b6f442469e7b18dcafaf03

On nightly, your .travis.yml, should use --check. Long-term, this is the strategy you should use, and it should be stabilized in a little less than a month.


Style across Nightly and Stable

Many projects, especially libraries, support both nightly and stable channels. This poses a unique problem for testing style because rustfmt has different checks in each channel’s version. This is because rustfmt is still in preview mode.

As a result, it’s best to pick one channel to enforce style.

Nightly style, stable code features

If you so desire, you can use rustfmt on the nightly channel, even if you only want to test your project on stable. It’s worth noting that you can’t do the inverse.

Here’s how:

Testing your code on the stable channel, but using `rustfmt` on nightly to test style. https://gist.github.com/ashleygwilliams/061eefdaf9a93480c7733cca736d5608

You’ll note that the Rust channel is set to stable but we pass --toolchain nightly when we add the rustfmt-preview component, and then also use +nightly when we call cargo fmt.

Communicate to your contributors

When deciding which channel to pick, you should pay attention to the channel your project’s contributors generally work on. I would recommend adding a section in your project’s CONTRIBUTING.md explains the need to choose one to enforce on. You can use this as a starting point (pulled from the wasm-pack CONTRIBUTING.md):

An example `CONTRIBUTING.md` with instructions for collaborators on how to setup `rustfmt` on nightly. https://gist.github.com/ashleygwilliams/3aba2a930adf312f12ba00f31979184b

You can also use a GitHub Pull Request template to help communicate this requirement to your users, for example (pull from the wasm-pack PR template):

An example PR template that helps remind users to run `rustfmt` on nightly. https://gist.github.com/ashleygwilliams/b5b62467d49f75284299126b758fd2c7

A Moving Target

As rustfmt works towards stabilization, another thing to be aware of is that the style checks in both stable and nightly will change. This can definitely be frustrating!

Updating rustfmt

If you are running into an issue with a contributor where style checks pass locally, but not on CI, it’s likely that the contributor has an older version of rustfmt. To update, run:

rustup update

Participate in the Discussion

If you are curious about what is in store for when rustfmt is stablized, or just want to see what lints might change, you should check out the following links:


And there you have it! Some best practices on using rustfmt to enforce style on your Rust projects in CI. Perhaps one day the whole developer community can relish, as I do, in the relief of leaving style enforcement energy to computers.