A Review of Cargo and Rustup
Part 1 of the Future of Cargo series.
DISCLAIMER: Sounds like the Cargo team is on the same page as some of my concerns are addressed through the Roadmap!
I am familiar with quite a few build tools and dependency managers from different languages. I mainly work with JVM languages so I am very familiar with SBT, Maven, Gradle, Leiningen and Ant, but I have also contributed to projects in Node.js (npm, gulp, grunt, …), Haskell (Stack, Cabal) and C (cmake, make).
I have chosen to review Cargo and Rustup together because most developers will use rustup to install Cargo and also because having the right toolchain matters a lot when building a Rust projects.
The Good Stuffs
One Tool To Rule Them All
As a beginner, the choice is easy and clear. No competitors, everyone seems happy with Cargo and it is officially supported by the Rust team. This definitely helps adoption.
Simple, easy and documented
Fairly straight forward, the documentation is quick to browse and tells you everything you need to know, rustup has a nice man page, Cargo TOML format is easy to understand and so far it hasn’t done anything I wasn’t expected.
Good Conventions / Nice To Have
All those little things which you wish that any language would provide. The test in the doc, the RLS, integration with github, Cargo.lock, …
The Not-So-Good Stuff
Built-in components
While I believe it’s really nice that Rust ship with components (doc, rls, …), I don’t like the fact that the doc is required and has to be downloaded everytime I install a new toolchain. Also components seems to be used for multiple purpose, it can be a tool (e.g `rls-preview-x86_64-apple-darwin`), a resource (e.g `rust-docs-x86_64-apple-darwin`) or a target (e.g rust-std-aarch64-unknown-fuchsia`). I have also noticed some unexpected behaviour while upgrading toolchains where the component wouldn’t be installed for the new toolchain.
The Bad Stuffs
Manual Toolchain management
A rust project is not linked to a toolchain. The only way to know which toolchain it’s supposed to run on, is, if not specified in the project documentation, to try a random one and hope it will compile. A lot of project are meant to run on nightly but nightly is a moving target, as its name suggest, it changes every night. A project compiling on nightly a year ago is unlikely to compile on nightly today, you’ll have to figure out yourself what toolchain make it work…
This also mean so if you have one dependency working on nightly, then you have to work on nightly too. If you have two dependencies on nightly, hopefully they are both working with the same nightly… That can lead to what I would call the Toolchain dependency hell…
Lack of extensibility / feature
Simplicity came at the price of extensibility. I can’t customize how test are run to, for example, have the code coverage of all my tests (including the doc one). There’s not really a plugin mechanism. Since its very limited in feature, it means that you need an other tool on top of it in a CI environment, to enforce linting rules etc…
There is sub-commands but it’s nothing else than a random executable. It doesn’t have special input from cargo, can’t change Cargo’s behaviour, etc…
Lots of Manual Steps
As a technical leader, I want my projects to have the smother on boarding experience. One of the thing to enable it, is to minimize the number of steps and requirment to run / compile the project. Ideally it should be 1. Clone the project, 2. Run this command. For a Rust project, there many steps : Install rustup, install the right toolchain, install the required components, then run cargo. It can of course be scripted but there’s no out-of-the-box option (like for example Gradle wrapper).
Dependency Requirements
Some dependencies only work on some architecture or require some external tools (gcc, python, …) and as far as I am aware nothing in Cargo is helping in detecting / mitigating that. You can easily end up with the “it works on my machine” problem.