Multiple versions of dependency in project

A dependency problem:

Let's assume the crate A depends on the crate B.
In my application, I use both A and B in the sense that I need to pass types from B to the functions of A. And I also want to use B directly in a totally unrelated part of the code.

Now let's assume B gets updated in a breaking way, and assume that crate A still requires the older B.

I want to use the updated version of B in one part of my code, but in the other part of my code, I need to pass types from the older B to A. How can I do that? Somehow I need to extern crate B both versions of B behind two different aliases, but I can't find out how to do that.

Thanks!

A set of legacy shims as seen in serde-rs/legacy would be one solution. Basically this consists of shim crates b1 and b2 that depend on their respective versions of b and do extern crate b; pub use b::*;. Then downstream crates can depend on both b1 and b2 and use types from both.

2 Likes

dtolnay provides a very good general suggestion. Are you only looking for general /and/or hypothetical solutions?
Or is there a concrete case you are running into? In general the rust-community tries to prevent exactly this from happening, so it would be nice to know if we overlooked anything :slight_smile:

@dtolnay Thank you!

@juleskers From time to time I see situations where I can not move to the latest version of a dependency because it is also a dependency of a dependency, like, during the last days the rand crate was updated which I use directly, but I also use mersenne_twister which depends on rand. In the meantime, also mersenne_twister moved to the latest rand, so everything is fine.
This just got me thinking how to navigate around potential problems in the future, but shim crates seems like the solution then.

1 Like

It isn't a good practice to depend on 2 separate versions imo. It can cause lot of issues while updating or if package breaks.

1 Like

Ah yes, something fundamental as rand will do it, yes :slight_smile:
Hopefully, now that the libz-blitz has brought quite a few central crates to 1.0, this should be less of a problem, as the stability-promises start to ensure backwards compatibility of the newer versions with the old.

interesting-but-useless-factlet: the jargon for this suffering, as used in RFC's causing it, is "upgrade churn", we care about it enough to have a word for it :wink:

That's not always something someone has control over; it can happen via indirect dependencies, for example. And sometimes your really need that new feature :slight_smile:
It is especially bad if the two versions are not mutually compatible, as happened quite a few times in the pre-1.0 crates. After all, 0.x versions are made for breaking changes..
It should become less of a problem as the ecosystem matures.

1 Like

Well true. But when you have control over it, you should avoid it if possible.

Just wanted to point out: I did not meant to criticize in my original post. Dependency management is simply hard and I think cargo is already a great tool. I just wanted to ask for best practices. :heart:

2 Likes

The other correct solution is that A should re export B iif B types appear in its public interface. This does not work for crates designed to facilitate intr-op like serde, but does give 2 names for the 2 different versions (A::B::Thing vs B::Thing).

1 Like

Probably best to mention the syntax for this (as perhaps not everybody is aware that extern crate is considered an item):

// yes, you can do this
pub extern crate rand;
2 Likes

This s is being implemented right now: https://github.com/rust-lang/cargo/pull/4953

5 Likes

[dependencies]
rand = {version = "0.7"}
rand04 = { version = "0.4", package = "rand" }

2 Likes