When you specify dependency requirements, please include the minor version, and for some crates also the patch version:
serde = "1.0.130" # good!
Cargo works well with versions specified this way. It is future-proof: the specified version is only the the minimum version, and still allows upgrades of minor versions. Cargo also unifies versions with the same semver-major number, so when two crates specify different versions, the higher version will be used for both (note that Cargo's behavior is quite different from npm's — specifying full versions is not a good idea in npm, but is fine in Cargo).
But DO NOT use imprecise depdendency requirements like:
serde = "1" # bad
Such overly-flexible version is very likely to be wrong, because it allows Cargo to use a completely different version from the version that you were using and testing against. It's easy to accidentally depend on a feature added sometime after the x.0.0 version. The crates-io ecosystem has popular crates that add new public APIs even in "patch" releases, so even a "1.0"
requirement isn't specific enough, despite intentions of the semver spec.
I know people use vague versions with good intentions to give flexibility to downstream users, but this backfires! Your crate may get a 5-year-old version of a dependency you've never tested. If you want to be maximally flexible with the versions you specify, please actually test them! (preferably in CI):
cargo +nightly -Z minimal-versions update
cargo test --all --all-features
or to test a specific downgraded dependency (and this works on stable):
cargo update -p serde --precise 1.0.0
cargo test --all --all-features
You may surprised how many crates following a "best practice" of imprecise versions end up specifying versions that are too low, and are incompatible.
Cargo may pick older versions, even in normal circumstances. It's not a bug, but a feature. If an existing project has a lockfile and adds your crate, Cargo will keep old versions of existing dependencies as long as they're "compatible". When you specify maximally "compatible" requirements, you will get the outdated versions.
Vague versions also create a second, counter-intuitive problem. You may be specifying very old dependency versions thinking of users of old Rust versions that can't upgrade their dependencies. The problem is that keeping less-than-latest dependencies in Cargo is hard. Without an existing carefully-preserved antique lockfile, it's a game of whack-a-mole of locking down versions of a dependency of a dependency. People who had to do it rightfully hate it. It would be sooo much easier with the -Z minimal-versions
option, but ironically, the imprecise versions break this feature! Vague versions are not helping old Rust users: they're breaking the best tool they could have.
Some people are worried that bumping dependencies to the very latest version can break if the latest version of a dependency is yanked. It doesn't break! Users who have a lockfile won't notice a thing. Users who don't have a lockfile, will temporarily get an older version of your crate instead. But solution to this is simple: specify a version one less than the latest (or some version that is a few weeks old). This mitigates the risk of the latest version getting yanked, without creating too much risk of a too-old version breaking the build.
It's actually pretty safe to bump deps to very latest versions:
cargo install cargo-edit
cargo upgrade
If you do this at the beginning of your development cycle (that may take weeks or months), by the time you release your crate these versions won't be so cutting-edge latest any more, but will be the best versions you've actually used and developed against.
Please, let's fix the crates ecosystem to work with -Z minimal-versions
. Specify dependency versions you've actually used and tested. If you insist on allowing many-years-old versions, don't make unverified assumptions — test them!