I go into more detail about the reasons in the rustbud README, but here’s an analogy:
The Python world has a system for isolating dependencies called “virtualenvs”: when you install a library in a virtualenv, it’s only available to programs and other libraries in that same virtualenv. The downside is that while Python packages can declare dependencies, they aren’t automatically synced with the virtualenv contents. It’s very easy to manually install a library to try it out, modify your code to use it, but forget to add it to the dependency list. When the next person checks out the project and tries to run it, boom, ImportError.
When I discovered Rust, Cargo was such an amazing upgrade from virtualenvs. You can’t use a library without first recording it in Cargo.toml, so your dependencies are always up-to-date. A whole class of problems eliminated, with zero additional effort!
Then I discovered rustup, where you can add toolchains and switch between them, without recording anything in the project you’re working on. While you can leave a rust-toolchain file lying around, it’s entirely optional, and it doesn’t say anything about what extra components you might have installed, or what tools you might have cargo-installed. It reminds me entirely too much of Python virtualenvs.
rustup is a fine tool, and it’s probably the right choice for most Rust developers, since it’s very “human friendly”—if you say “cargo build” then rustup will build with something, and trust that if it picks the wrong thing, a human will be there to hit Ctrl-C and give more detailed instructions. But for automated environments with no humans around, that kind of friendliness causes more problems than it solves, so there’s a need for something stricter. I doubt that something strict could be built on top of something flexible like rustup, but I’m sure rustup could be built on top of something strict.