Description
This is an issue that has been bugging me with cargo for a while now, so I wanted to ask if it would be possible for cargo to support the following:
Suppose there is a library sometinycrate
. sometinycrate
exposes TinyImportantStruct
in its public API. Because sometinycrate
is very popular, it gets picked up by the larger library bigwrappercrate
. So bigwrappercrate v0.0.1
has now a dependency on sometinycrate v0.0.1
.
Now I want to build a binary and use bigwrappercrate
. bigwrappercrate
has a function that takes a TinyImportantStruct
by value. The problem now is: how can I create a TinyImportantStruct
, assuming that bigwrappercrate
does not re-export the TinyImportantStruct
(which happens often)? There are two options:
- Copy-and-paste the
sometinycrate = "0.0.1"
into the binarysCargo.toml
(bad ifbigwrappercrate
updates tosometinycrate = "0.0.2"
, then my binary build will break) - Tell the original crate maintainer to reexport the given type or the whole
sometinycrate
(bad if he's not available)
Note that macro re-exporting does not work on a crate-level basis. Hence, for macro-heavy libraries (let's say serde-derive
) I have to use Option 1. Now, usually, this problem can be solved by just doing this:
// sometinycrate
pub struct TinyImportantStruct;
// bigwrappercrate
extern crate sometinycrate; // Option (1) <- bad!
pub extern crate sometinycrate; // Option (2) <- better, but still not perfect, doesn't work for macro crates
Now, in reality, Option 2 is very rare. Nearly nobody re-exports crates.
With Option 1, I now have this setup:
# mybinary, Cargo.toml
bigwrappercrate = "0.0.1"
sometinycrate = "0.0.1" # <- manual lookup, bad!
# I have to (manually) lookup which version of sometinycrate bigwrappercrate is using!
# The idea is to automate this lookup by using cargo!
// mybinary, main.rs
extern crate bigwrappercrate;
extern crate sometinycrate;
fn main() {
let struct = sometinycrate::TinyImportantStruct
}
Option 2 is this:
// mybinary, main.rs
extern crate bigwrappercrate;
fn main() {
let struct = bigwrappercrate::sometinycrate::TinyImportantStruct
}
The problem with this is that Rust (rightfully) thinks of TinyImportantStruct
in v0.0.1
and TinyImportantStruct
in v0.0.2
as completely different structs and therefore won't compile on a version mismatch.
How cargo could help
My solution to this problem would be to basically do Option 1, but automate the lookup using Cargo, using a syntax like this:
# instead of:
bigwrappercrate = "0.0.1"
sometinycrate = "0.0.1" # <- manual lookup, bad!
# you should be able to do this:
bigwrappercrate = "0.0.1"
sometinycrate = "bigwrappercrate" # use whatever version bigwrappercrate is using
# if the dependency is more nested:
sometinycrate = "bigwrappercrate::otherdependency::middleware"
# "middleware" must have a dependency called "sometinycrate"
Don't get too stuck on the syntax, this is just about the idea. Now, let's see what happens if bigwrappercrate v0.0.2
releases, which depends on sometinycrate = "0.0.2"
. With the manual method, I have to update mybinary
's Cargo.toml to change sometinycrate = "0.0.2"
- otherwise, it won't even compile. With the proposed method, this is done for me.
Here and here are practial applications of this problem. Thanks for reading.