Skip to content

enhancement - allow to express the version of a crate as a version of a dependent crate #4641

Closed
@fschutt

Description

@fschutt

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:

  1. Copy-and-paste the sometinycrate = "0.0.1" into the binarys Cargo.toml (bad if bigwrappercrate updates to sometinycrate = "0.0.2", then my binary build will break)
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-dependency-resolutionArea: dependency resolution and the resolverC-feature-requestCategory: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions