Accessing crates within crates


#1

I’m maintaining a library that provides partial high-level abstractions of another library (glium_sdl2 -> SDL2).

Suppose I have the libraries foo and bar. foo is a utility library that extends (but doesn’t replace) the capabilities of library bar.
The binary myapp needs to have access to both crates simultaneously. It is desirable for users to continue using bar as an autonomous crate, and for foo to call into and accept data from bar.

myapp/src/main.rs

extern crate foo;
// How do I access the `bar` crate, as used by `foo`?
extern crate bar;

let b = bar::Bar::new();
foo::consume_bar(b);

myapp/Cargo.toml

[dependencies]
foo = "0.1"

foo/Cargo.toml

[package]
name = "foo"
version = "0.1.1"

[dependencies]
bar = "0.5"

bar/Cargo.toml

[package]
name = "bar"
version = "0.5.8"

Is there a way to refer to foo::bar inside the program without hard-coding what the version is supposed to be in myapp?
It’s important not to have to add something like bar = "0.5" at the end of myapp/Cargo.toml, in the case that foo = "*".

Sorry if this is worded poorly.


#2

Does putting bar = "*" in the myapp's manifest not work? foo will still request 0.5, myapp says it doesn’t care so 0.5 should satisfy it as well.


#3

That depends if Cargo is smart enough not to simply get the latest version of bar alongside 0.5, which I’m unsure of. (suppose the latest bar is at version 0.15.3 or something).


#4

AFAIK it doesn’t support using two versions of the same crate simultaneously (from crates.io, it’s different with git and path dependencies).


#5

Just saying: cargo completely supports having two versions of the same crate simultaneously from crates.io IIRC. I would think having “*” would work though to just use the one foo uses.


#6

NB. Cargo strongly encourages the use of semantic versioning (well, a slight variant), which means that libs that go from 0.1 to 0.2 (for example) or 1.0 to 2.0 may have arbitrary breaking changes. In particular, there’s absolutely no guarantee that the library will compile with anything other than the requested versions of dependencies. Using a * version constraint is hence discouraged.

However, cargo does have various ways to mitigate the problem: a version constraint like 0.5 is satisfied by any version 0.5.x, and 0.5.8 is satisfied by any version 0.5.y with y >= 8. Cargo will try to minimise the number of copies of a library by chosing versions that work best.

Also, as @daboross says, rust and cargo are designed to allow using crates with multiple different versions.


#7

I mentioned foo = "*" more for illustrative purposes, to account for any case where the dependency versions may change (not indicating that "*" should be liberally used by any means). Some users may also want to be on top of breaking changes when they happen, especially for unstable libraries, so "*" is sometimes appropriate.

In my case, foo could make breaking changes simply by updating its dependency versions. If bar gets bumped to "0.6" (a major change), then foo would be required to bump to "0.2". I’d like a way to refer to bar's new version in my application for free, without explicitly writing it out in my manifest.
Likewise, if I specify foo = "0.1", then bar still = "0.5". In other words, the version of foo should determine the version of bar.

If breaking changes aren’t frequent, this doesn’t pose much of a problem at all. However, because my library is unstable, I’d like users to be able to elect to sudden changes with semver ranges such as ">= 0.2" or *.