Why doesn't semver caret mitigate dependency conflicts?

Rust's Caret semver is great at enabling the use of any greater version that does not increment the major version (as documented).

However, I assumed this meant that if crate x depends on crate y, then crate x could list patch versions higher than crate y. I was miserably wrong apparently.

I've found it necessary for crate y to instead specify versions like 1.2.* instead of ^1.2.3 so that crate x does not have to also specify the same patch version:

crate y Cargo.toml:

[package]
name = "y"
version = "0.0.1"
edition = "2021"

[dependencies]
serde_json = "1.0.*" # if I left it plainly as 1.0.22, it breaks crate x build

crate x Cargo.toml

[package]
name = "x"
version = "0.0.1"
edition = "2021"

[dependencies]
serde_json = "1.0.107"
y = { git = "ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/crate-y", branch = "main" }

But this allows any patch version, which isn't what I want. I can probably trust developers not to use really old patch versions, but I'm curious why isn't there a way to use caret for mitigating inter-crate dependency conflicts such that a version greater than or equal to the lowest patch value is chosen?

This seems to be incorrect. The dependency "1.0.22" is compatible with "1.0.107", so your build will be able to use the version "1.0.107" when you specify "1.0.22" in y.

As for the comment of mine you quoted, please notice that they have equals signs in them. Equals signs do cause dependency conflicts, but that's different from carets.

2 Likes

@alice thank you it took me a while to stub my toe again on a version conflict in the wild rather than the contrived example I had.

...So in other words this is clearly an XY problem and thank you for responding! Now for a more interesting account of the problem:

error[E0308]: mismatched types
  --> proj_x/src/main.rs:27:36
   |
27 |             let client = s3_client(&config).await;
   |                          --------- ^^^^^^^ expected `aws_types::sdk_config::SdkConfig`, found a different `aws_types::sdk_config::SdkConfig`
   |                          |
   |                          arguments to this function are incorrect
   |
   = note: `aws_types::sdk_config::SdkConfig` and `aws_types::sdk_config::SdkConfig` have similar names, but are actually distinct types
note: `aws_types::sdk_config::SdkConfig` is defined in crate `aws_types`
  --> /home/me/.cargo/registry/src/index.crates.io-6f17d22bba15001f/aws-types-0.55.3/src/sdk_config.rs:48:1
   |
48 | pub struct SdkConfig {
   | ^^^^^^^^^^^^^^^^^^^^
note: `aws_types::sdk_config::SdkConfig` is defined in crate `aws_types`
  --> /home/me/.cargo/registry/src/index.crates.io-6f17d22bba15001f/aws-types-0.56.1/src/sdk_config.rs:49:1
   |
49 | pub struct SdkConfig {
   | ^^^^^^^^^^^^^^^^^^^^
   = note: perhaps two different versions of crate `aws_types` are being used?
note: function defined here
  --> /home/me/.cargo/git/checkouts/aws-support-db9a0d26fa48aea9/4a1606c/proj_y/src/operation.rs:14:14
   |
14 | pub async fn s3_client(config: &SdkConfig) -> Client {

The problem summarized is that proj_x has version 0.55.3 of aws_types from a separate dependee but proj_y (also a dependee of proj_x) has version 0.56.1

So, to rephrase my question: why can't Cargo automatically figure out to use the lower of the two versions? The only workaround I've managed is to instead always wilcard my versions to guard against such conflicts, but this opens me up to a very wide range: aws_types = "0.*.*"

Cargo considers 0.55.3 and 0.56.1 to be incompatible. It basically skips any leading 0 and treats the 55 like it's the major version. proj_x will never try to use any 0.56 version.

If you want to use 0.56, you'd need to patch whatever dependency uses 0.55.3 to use some 0.56 instead.

If you have control over the version number and want to use either 0.55 or 0.56, so that you get 0.56 when your dependency eventually updates, you can use an inequality: >=0.55, <0.57

Caret syntax is the same as having no symbol. So 1.2.3 is the same as ^1.2.3.

3 Likes

Thank you, I think I'm yet another developer frustrated by caret exceptions with semver regarding the magic zero.

I'm glad Cargo supports inequality expressions, that somehow evaded my attention until now!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.