Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
fschutt opened this issue Oct 18, 2017 · 3 comments
Labels
A-dependency-resolution Area: dependency resolution and the resolver C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`

Comments

@fschutt
Copy link
Contributor

fschutt commented Oct 18, 2017

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.

@alexcrichton alexcrichton added the C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted` label Oct 18, 2017
@carols10cents
Copy link
Member

carols10cents commented Oct 18, 2017

I believe this should be solved by public/private dependencies, which is an accepted RFC that has not been implemented yet (tracking issue).

In the case you have outlined above, bigwrappercrate would declare a public dependency on sometinycrate since a sometinycrate type appears in bigwrappercrate's public API. bigwrappercrate's Cargo.toml would look like this:

[dependencies]
sometinycrate = { version = "0.0.1", public = true }

Then, in your crate, you'd have a dependency on sometinycrate and a dependency on bigwrappercrate. Because of the public dependency bigwrappercrate has on sometinycrate, any dependency graph resolution where different versions of sometinycrate could reach each other would be considered invalid. If no resolution was possible, cargo would tell you to change your version constraints, rather than rustc giving you a compile time error like expected TinyImportantStruct, got TinyImportantStruct.

I think that should solve 80% of what you're describing here, the other part would be an ability to specify in your Cargo.toml once that you always want to be using the version of sometinycrate that bigwrappercrate is using, so that you never have to manually update the version restriction in your Cargo.toml. That was discussed in #1636, and @alexcrichton said at the time:

I'd personally prefer to avoid the case of explicitly telling Cargo "use the version this library is using" and instead just have it automatically deduce that

(deduce that through the public/private resolution changes). Is that still your opinion, @alexcrichton ? If so, I believe this issue can be closed as resolved by public/private dependencies.

@carols10cents carols10cents added the A-dependency-resolution Area: dependency resolution and the resolver label Oct 18, 2017
@alexcrichton
Copy link
Member

@carols10cents indeed I'd still think the same!

@carols10cents
Copy link
Member

Ok, then in that case I'm going to close this as something that will be solved once public/private dependencies are implemented. Please let me know if there's something I'm missing @fschutt !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-dependency-resolution Area: dependency resolution and the resolver C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`
Projects
None yet
Development

No branches or pull requests

3 participants