Is there really (still) no way specify a dependency of a dependency

I have read this question/answer as well as this SO question/answer. But it is now 2021.

Is there a way to specify a dependency of a dependency?

As an example, I cloned https://github.com/abonander/multipart.git and changed:

tiny_http = { version = "0.6", optional = true }

to

tiny_http = { version = ">=0.6", optional = true }

in its Cargo.toml. Then I made a very simple new project with this as its Cargo.toml:

[package]
name = "test-multipart"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tiny_http = "0.7.0"
multipart = { path = "../multipart", features = ["server", "tiny_http" ] }

and a copy of multipart's examples/tiny_http.rs as its main.rs. When I attempt to build it, the build fails, and I see this in the output:

$ cargo build
...
   Compiling tiny_http v0.10.0
   Compiling tiny_http v0.7.0

It seems like the compiler is choosing the latest version of tiny_http (v0.10.0) when compiling multipart, but selecting (as I specified) v0.7.0 of tiny_http when compiling my code. How can I tell the compiler to use v0.7.0 for both crates?

I know how I could tell it to use v0.10.0 for both crates (just change my crate). But, how would I tell it to use v0.7.0 for both crates?

I hope I have explained this well. It seems like the two references I cited above asked the same question, and the answer (back then) was "you can't". I'm curious if that answer has changed. I am also curious to understand why that answer hasn't changed. It seems like I am not the first, nor only, person to have run into this problem.

AFAIK, the short answer is still no.
The longer answer would be why? Well, for starters, the compiler is indeed following the constraints correctly, and so it is doing the correct thing. The more important thing is that if you could specify versions of transitive dependencies, I think it is akin to tampering what the original library's author intended. If the author specified a dependency version of >=0.6, then they wanted the latest version of that crate, as long as it is at least as recent as 0.6. Not 0.7, not 0.8 but the latest. Maybe the author anticipates performance increases with latest versions of that library and hence put in that constraint.

2 Likes

0.7.0 and 0.10.0 are considered incompatible versions of the library and can't be swapped for each other.

This would do what you wanted if the versions were 1.7.0 and 1.10.0, or if the versions were 0.7.0 and 0.7.12 .

4 Likes

I guess I am thinking that if the author of the crate said "This crate will work with any version of tiny_http as long as the version is >= 6, and I said "How about version 7?", then we (as negotiated by the compiler) would both agree that version 7 was acceptable.

If 5 years from now, version 13 of tiny_http comes along that is incompatible with previous versions, then I could say, "How about version 12?" and we would both agree that version 12 was acceptable.

I am working on a PR for multipart that will address this... So far my PR consists of changing the version for tiny_http from 0.6 to >=0.6, and I just want to make sure I don't break anything by doing so (such as linking in tiny_http version 10 in a space constrained embedded application that used to work fine with version 6). So I ask questions in order to try to learn how to do things better.

At the moment, it sounds like just changing the version from 0.6 to >=0.6 is probably a suboptimal way to go. So I will continue to investigate better ways to approach this.

Thanks for the answers. Please keep them coming.

--wpd

I would place some of the blame on multipart for this one.

Each public dependency (i.e. one which is used in public API) of a crate is a responsibility. The maintainer has a responsibility to ensure that this dependency does not hold the create back due to versioning difficulties.

This can be achieved through a number of ways:

  1. Investigate how stable the crate's version number is. Look at it's version history, see which changes provoked a major version bump. Look at it's owners. If, for instance, I see a 1.0 crate by David Tolnay then I know it's in good hands and I can reasonably expect it to present little to no issues in the future.
  2. In some cases it may suffice to re-export the dependency at the crate root, so that users could use multipart::tiny_http. It depends on the expected use cases for interop.
  3. Provide methods which do not use the types of the external dependency in the public signature, taking simpler arguments instead. If people are likely to want to use types from that dependency, provide example snippets showing how to call your methods from those types.
  4. If all of the above fail and you must add a volatile, public dependency (often the case with rand), then it is your duty to keep it up to date as the ecosystem demands.

multipart has multiple PRs, one from two years ago just to update tiny_http, and these haven't been merged. I think this is a bit irresponsible.


Using >= on a dependency is uncommon in the rust ecosystem, and I think there's a fair reason for this. If a new version of tiny_http came along which broke the code inside multipart which uses tiny_http, then simply adding multipart to a crate with the tiny_http feature would make a crate fail to compile until the user added their own, lower tiny_http version (apparently not, see next post...).

This is the correct behavior for private dependencies; public dependencies are special in that you do want to solve for everyone using the same version.

So while dependency constraints across multiple major versions are valid (e.g. tiny_http = ">=0.6.0, <0.11.0"), you'll always get the most recent major version (e.g. 0.10) with either of the current resolvers.

3 Likes

Hello @ExpHP

The author of multipart clearly states in README.md:

Maintenance Status: Passive
As the web ecosystem in Rust moves towards asynchronous APIs, the need for this crate in synchronous API form becomes dubious. This crate in its current form is usable enough, so as of June 2020 it is now in passive maintenance mode; bug reports will be addressed as time permits and PRs will be accepted but otherwise no new development of the existing API is taking place.

You call the author of multipart irresponsible. Without knowing anything more about this, I might suggest that the author is weary... weary of the responsibility of updating a crate that (apparently) has very little usage (except for tiny corner cases such as mine).

I guess I could offer to take over the maintenance of this crate (or perhaps fork my own copy)... but I suspect I too would grow weary (IOW not remember to pay attention to) requests to update the crate every time that tiny_http, hyper, iron, nickel bumped a version.

Or, I could just copy and paste the code into my project and not deal with this issue anymore.

Fundamentally, I am trying solve the problem that when I build a project that uses tiny_http of one version and multipart that uses tiny_http of a different version, I get this error:

error[E0277]: the trait bound `&mut tiny_http::Request: multipart::server::HttpRequest` is not satisfied
   --> src/server.rs:157:35

I understand the reason for this error: Rust treats tiny_http::Request from version 7 of tiny_http is a different type than the tiny_http::Request from version 10 of tiny_http. It seems to me that one way to address this issue (especially for a project in passive maintenance mode) is for multipart to say "you can use this with any version of tiny_http you want, but we have only ever tested it with version 6... the rest is up to you".

It seems like @CAD97 is dealing with the same sort of issue (although perhaps without the "passive maintenance" twist). It just seems to me that there should be some way for me to say "In this particular case, I know what I'm doing and I want to do this". (I recognize the hubris of that statement, as truly, I don't know what I'm doing here, but I can dream :slight_smile: ).

So, one way to say "I know what I'm doing" is to edit my Cargo.lock file and move forward from there.

Continuing to look for advice and suggestions from the experts...

Thank you so much.

--wpd

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.