Say I have a crate A that depends on zstd = ^0.6 (only used internally) and a crate B that depends on A and zstd = ^0.6 (also only used internally).
Crate B uses ^0.6 because it matches A (smaller binary, etc.).
A confirms that it is compatible with ^0.9 and would like to offer support also for this in their next release (e.g. 0.9 is faster than 0.6).
How should A proceed?
change ^0.6 to ^0.9.
change ^0.6 to >=0.6,<0.10
From the Python ecosystem, we generally favor 2. because it offers downstream consumers the flexibility to choose how they wanna do it. However, Python does not allow 2 packages with different versions.
Option 1. seems the preferred option in Cargo, but my impression is that we "force" everyone to follow suit or increase the binary size (the same way B was "forced" to use ^0.6 in the first place).
I normally just update the requirement in my crate's Cargo.toml file and let the dependency update whenever they want. For 99% of use cases you honestly don't care about binary size (and the increase will be marginal anyway, so you'd barely notice if cargo compiled both v0.6 and v0.9 of the dependency.
Running cargo outdated and cargo update every now and then also helps tidy up these sorts of duplicated versions because it lets you upgrade all your dependencies to the latest version.
The only situations I can think of where you might care are...
Your platform has limited memory and you need to squeeze out every last kb from your binary
The resulting binary ends up massive (e.g. hundreds of MB)
Compiling both versions of the dependency has a non-trivial effect on build times (e.g. wasmer or tokio)
When you need to pass objects defined in v0.6 of zstd to something that expects the v0.9 version
The biggest downside I can see to relaxing restrictions is that those versions may not actually be semver compatible and your users run into compilation issues because the resolver chose to use a particular version.