4

By forking and playing with some code, I noticed that Cargo can download and bundle multiple versions of the same crate in the same project (native-tls 0.1.5 and 0.2.1, for example). I have wasted so much time by looking at the documentation of the wrong version.

I have looked for some information about this behaviour but I was not able to find anything. Is this documented somewhere?

Is there an easy way to determine/detect the version used by the code you're working on (current edited file)? Or can we tell Cargo to show some warnings/prevent the build if two versions the same crate are required?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
yageek
  • 4,115
  • 3
  • 30
  • 48

2 Answers2

10

It was a conscious decision when designing Rust to allow multiple versions of the same crate.

You have probably heard of Dependency Hell before, which occurs when 2 (or more) dependencies A and B have a common dependency C but each require a version which is incompatible with the other.

Rust was designed to ensure that this would not be an issue.

In general, cargo will attempt to find a common version which satisfies all requirements. As long as crate authors use SemVer correctly, and the requirements give enough leeway, a single version of the dependency can be computed and used successfully.

Sometimes, however, multiple versions of the same dependency are necessary, such as in your case since 0.1.x and 0.2.x are considered two different major versions. In this case, Rust has two features to allow the two versions to be used within the same binary:

  • a unique hash per version is appended to each symbol.
  • the type system considers the same type Foo from two versions of C to be different types.

There is a limitation, of course. If a function of A returns an instance of C::Foo and you attempt to pass it to a function of B, the compiler will refuse it (it considers the two types to be different). This is an intractable problem1.

Anytime the dependency on C is internal, or the use of C is isolated, it otherwise works automatically. As your experience shows, it is so seamless that the user may not even realize it is happening.

1 See the dtolnay trick that crate authors can use to allow some types to be interchangeable.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
3

Cargo can indeed link multiple versions of some crate, but only one of those versions can be a direct dependency. The others are indirect references.

The direct reference is always the version referenced by Cargo.toml and on top-level of Cargo.lock (while the indirect references are in the dependencies subsections).

I am not sure how much it is documented, unfortunately.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • Note that with renaming dependencies multiple versions of the same crate can now be a direct dependency, too – kert Sep 20 '21 at 17:54
  • In version 3 of Cargo.lock, "top level of Cargo.lock" and "dependencies subsections" no longer exist. Each top level package in the workspace has its own "package" entry listing its direct dependencies, which could include a version like `"base64 0.20.0"` which needs to be split, and then located within the lock file. – John Vandenberg Jan 09 '23 at 01:35