Should I pin my Rust toolchain version?
— 5 minThe short answer is: yes. And the main reason is to make updates intentional and avoid surprises.
For a long time, I have been chasing the very latest Rust version (I still am though),
and I have been copy-pasting rustup toolchain install stable
around my CI workflows.
By now, I have changed my opinion on this, and I would recommend to pin the Rust toolchain to a specific version using
a rust-toolchain.toml
file:
[toolchain]
channel = "1.84"
profile = "minimal"
I still think that you should chase the very latest Rust version, and to frequently update the version you use. But it should be an intentional change, and not come as a surprise to anyone.
Forcing everyone onto the same toolchain version, and making updates intentional, you will avoid friction both for new and old contributors.
Rust has amazing stability guarantees, and you can update both the toolchain and dependencies (cargo update
) without fear.
But doing such updates can introduce new lints or deprecation warnings. In particular clippy
is frequently introducing new lints.
In the interest of code quality, I highly recommend enforcing these lints as well, and making sure your crate compiles without warnings.
Enforcing lints, combined with automatically pulling in new lints every 6 weeks however is causing friction. It means that contributors’ PRs are failing for unrelated reasons. And it means that those contributors are being forced to fix those unrelated lint warnings. This often means that you end up with completely unrelated changes in PRs. Sometimes even the same changes in multiple PRs from different contributors.
This is certainly not a nice experience, and it can be avoided by pinning the toolchain version. And then updating the toolchain and fixing those lints intentionally in a dedicated PR, or fixing lints in a different PR before bumping the toolchain version.
Even if you do not enforce clippy
lints within CI, the same friction also happens the other way around.
Maybe a contributor is seeing the latest clippy
lints locally in their editor thanks to having rust-analyzer
configured to do so.
So even if your CI is not enforcing it, you might end up with unrelated changes in contributor PRs.
Another example of friction is using the latest and greatest Rust features. Again, I do advocate that you do :-)
But using a newly stabilized API immediately the day it becomes available also means that other contributors are forced to update.
Those who haven’t updated to the latest toolchain version yet are presented with a compiler error.
Frustrated, they are asking colleagues about it, thinking they did something wrong.
They didn’t, and they can fix the problems simply by running rustup update
. But it is still a bad experience for them.
By pinning a specific toolchain, rustup
will manage updating to a newer toolchain for you, without any friction.
Even for teammates who haven’t yet made it a ritual to read the release notes and run rustup update
every six weeks :-)
This also works the other way around. Having pinned the toolchain version means that developers living on the edge do not accidentally introduce calls to functions that haven’t been stabilized yet (in the pinned version). It might also serve as a reminder to bump and update the pinned version.
# A note on dependencies
I believe it might be a good idea to think of the toolchain as yet another dependency. And pinning the toolchain to a particular known version is very similar to the Guidance on Committing Lockfiles.
On this note, there has been some recent chatter once again in the Rust community related to the over-use of dependencies.
I do agree that it is bad to introduce new dependencies lightheartedly, and to have dependencies just for the sake of dependencies.
We should work towards moving more functionality into std
where it clearly makes sense to do so.
We should avoid useless dependencies like left-pad
or isarray
.
On that note, I am shocked to see how its even possible that these packages still have millions of weekly downloads. Like, what the hell, really?
To give a concrete Rust-specific example, since its inclusion in std
, the only valid reason to still depend on once_cell
would be to support a MSRV prior to its stabilization.
And there is no reason at all to still use lazy_static
which is its spiritual predecessor.
However, I disagree with the idea that once you have a dependency, you shouldn’t touch it.
I do believe that you should do both rustup update
and cargo update
frequently.
Yes, there is the whole software supply chain security issue, which is indeed a problem, and I don’t know of a good solution to it.
But the Rust ecosystem is very blessed with a good story around stability, compared to other ecosystems.
We are lucky to even be able to do a rustup update
or cargo update
without stuff breaking.
Yes, there might be new lints, and new deprecation warnings. But that is part of maintenance, and it should be done.
On that note, I have begun writing a new rust-maintain
GitHub Action, which should help to automate as much of this process as possible.
However, it is not yet functional, and there are currently some issues that prevent it from working.
I guess by announcing it in such a way, I kinda keep myself accountable to eventually finishing this up and making it useful.
Well, once I gather a bit of motivation :-)