> You don't have to fight dependencies. It just works when you cargo build.
Unless you're using caret requirements, or tilde requirements, or wildcard requirements, or inequality requirements, or multiple version requirements, or dependency overrides.
By default, Rust, much like every other language I know of, does not require explicit versions or checksums for dependencies. This means it's up to the developer to work really hard to make sure their code will actually work as expected outside of their own build environment. This could be mostly avoided if more people used explicit version requirements in their code, but nobody seems to want to do this (which is strange, because your code is what needs specific deps, so why not keep the version requirements right there?)
edit: For all the duplicate comments mentioning cargo.lock: The official FAQ details how libraries do not impose requirements at build time (https://doc.rust-lang.org/cargo/faq.html#why-do-binaries-hav...): "Users dependent on the library will not inspect the library’s Cargo.lock (even if it exists)". The user cannot be certain their app will always work, because they can't be certain that their dependent library has been tested on each other library dependency. Basically, there's no certainty if you use libraries.
The other thing people are missing is how being able to call duplicate conflicting versions of the same library where it is needed in your code would allow you to work around complicated bugs in large projects automatically, rather than having to find a magic combination of conflicting dep versions that work for your app and its dependencies. Apparently nobody here has ever tried to manage more than one OpenStack tools tree for use by the same application.
Caret, tilde and other loose requirements are fine in Cargo.toml because cargo generates Cargo.lock on your behalf with the exact versions required for your build. This is how pretty much all modern dependency managers work(Bundler, Composer, NPM, CocoaPods, SPM, etc). Saying that this doesn't work just is not true.
All you have to do is keep your Cargo.lock file in version control, and you will have consistent dependency versions in any environment. This will work regardless of how generally you specify the version requirements of your dependencies.
> > call duplicate conflicting versions of the same library where it is needed in your code
>
> Cargo already does this automatically if two of your dependencies depend on conflicting versions of the same library.
Assuming this is the case, your code doesn't specify which version of what function to use. So how could Cargo know which to use?
To be clear, Cargo will attempt to unify versions as much as it can. If it can't, you'll get multiple copies.
For example, let's say my project depends on two libraries; A and B. Both depend on library C.
If A depends on C version ^ 1.0.0, and B depends on C version ^ 1.1.0, and 1.2.0 is the latest version of C, then both A and B will end up with C 1.2.0.
If A depends on C = 1.0.0, and B depends on C version = 1.1.0, then you'll get two copies of C.
And when A calls something in B? For this example, let's bump the major rev.
B is expecting C = 2.0.0. A is expecting C = 1.0.0. A makes a call to B, which it passes to C. Does A know that B now expects to be called differently, since C is also significantly different now? According to the above requirements, no version of the deps exist that could allow the app to execute correctly, even though it will technically build fine.
The way to resolve this would be to say A requires C < 2.0.0, and simply fail to build until someone fixes A. But you probably won't even know about this conflict until someone tests the app with a specific feature of A that conflicts with C >= 2.0.0, the build fails, someone figures out the conflict, and updates the requirements.
But you can only know this, and add this requirement, once you have found the conflict. Thus code in the present, even with dependency requirements, may be indeterminate in the future. (Unless you walk back through all calls in the code to find calls between multiple dependencies that eventually land on conflicts... but I don't think that is possible in Turing-complete languages)
If you pinned the version in each function call in each part of the code, you could have the compiler or interpreter walk back through dependent code, identify mismatches, retrieve a compatible dependency, and continue execution. Or at the very least throw warnings all over the place when code is running using dependencies it wasn't written for.
You get a type error, which was actually funny, given that the message used to say something like "expecting type T, got type T".
Funny enough, what you describe is theoretically possible in JavaScript, but strangely enough, it seems to work... it's something I've long been trying to come up with a POC exploit for.
Unless you're using caret requirements, or tilde requirements, or wildcard requirements, or inequality requirements, or multiple version requirements, or dependency overrides.
By default, Rust, much like every other language I know of, does not require explicit versions or checksums for dependencies. This means it's up to the developer to work really hard to make sure their code will actually work as expected outside of their own build environment. This could be mostly avoided if more people used explicit version requirements in their code, but nobody seems to want to do this (which is strange, because your code is what needs specific deps, so why not keep the version requirements right there?)
edit: For all the duplicate comments mentioning cargo.lock: The official FAQ details how libraries do not impose requirements at build time (https://doc.rust-lang.org/cargo/faq.html#why-do-binaries-hav...): "Users dependent on the library will not inspect the library’s Cargo.lock (even if it exists)". The user cannot be certain their app will always work, because they can't be certain that their dependent library has been tested on each other library dependency. Basically, there's no certainty if you use libraries.
The other thing people are missing is how being able to call duplicate conflicting versions of the same library where it is needed in your code would allow you to work around complicated bugs in large projects automatically, rather than having to find a magic combination of conflicting dep versions that work for your app and its dependencies. Apparently nobody here has ever tried to manage more than one OpenStack tools tree for use by the same application.