I wouldn't call that "versioning hell". As long as the compiler builds an application that works, isn't that the opposite of any kind of "hell"?
As for building multiple versions of the same crate, it's practically unavoidable. This is a feature of Cargo, which allows the application to build, instead of creating a real "dependency hell" that causes builds to fail because of breaking API changes.
Using the example you provided, the public API exposed by crate C version 2.0 is almost certainly incompatible with the public API exposed by crate C version 1.0 (following semver conventions). So trying to build only one version of the crate in a diamond dependency graph will fail with very high probability. Even if by some chance it succeeds now, what happens when you pull in another dependency later which (perhaps transitively) depends on crate C version 3.0? Now you're in real hot water. The only way to fix this is to get the maintainers of crates A and B to update to the latest version of crate C. You might want to fork them both and do this upgrade, referencing your forks in
But I don't think this is really the question you are asking. You want to reduce the binary size. Ok, so there are a few things you can do for that. First, check out the invaluable min-sized-rust repo to help you strip out most of the binary bloat that Rust provides by default. And second, perhaps far more importantly, install
cargo-bloat and use it to find the biggest offenders.
The next steps are a little subjective and definitely needs to be handled on a case-by-case basis, but these may be useful tips...
In my case,
cargo-bloat told me that half of my executable was from
goblin's Object file parsing for Windows and MacOS, which I knew I did not need (I only wanted ELF parsing). A quick dig through the docs and I discovered that using
goblin::Object::parse() pulls in all of the Windows PE and Mach parsing and handling code, and that I could get rid of it by using the more specific
goblin::elf::Elf::parse() directly. TL;DR: half of my binary size was dead code and it was easy to find with
In another project, the biggest contributor to bloat was
clap. At which point I realized that I don't need
clap at all, because
std::env is good enough, even if the code doesn't look quite as pretty.
clap was adding like 100KB of uselessness to my 25KB executable.