Why does adding byteorder make Cargo downgrade mysql to version 8.0.0? - rust

I've been developing a project in Rust for awhile. A few days ago I ran cargo update and a whole bunch of my dependencies got downgraded, and I haven't been able to figure out why. I created a new project and have found that if the dependencies in Cargo.toml are just
[dependencies]
mysql = "*"
it builds with the latest mysql (11.3.0) as I would expect. If I add
byteorder = "1"
then run cargo clean/cargo update, mysql gets downgraded to 8.0.0.
Any help figuring out why the byteorder dependency is making Cargo downgrade mysql or how to stop it from doing so would be appreciated.

how to stop it from doing so
This is the easy part: don't use wildcard versions. The chances of your code working with literally any version of that crate that has ever been published is, on average, zero.
why the byteorder dependency is making Cargo downgrade mysql
This is actually really hard to answer. Picking dependencies is an NP-hard problem. Since most programmers don't care to wait that long, there are heuristics and preferences and shortcuts in every dependency manager. I don't know all the nuances of Cargo's algorithm, so most of this is educated guesses or investigation.
You've told Cargo "I don't care what version mysql to use" by saying mysql = "*". Cargo is now free to use whatever version it wants to, a very flexible requirement.
In this case, mysql 11.3.0 has chosen to require byteorder = "~1.0". That does not allow byteorder 1.1.0. Some aspect of the dependency resolution sees this and says it'd be better to allow your crate to have version 1.1.0 of byteorder, even if that means that mysql needs to be downgraded to a non-conflicting version. The important thing is that version 8.0.0 was the last version that only requires byteorder 0.5.3.
If you try to force both to the current versions, you'll see this:
error: failed to select a version for `byteorder` (required by `mysql`):
all possible versions conflict with previously selected versions of `byteorder`
version 1.1.0 in use by byteorder v1.1.0
possible versions to select: 1.0.0
However, you can get almost fully updated:
[dependencies]
mysql = "11.3.0"
byteorder = "1.0.0"
I'm not fully sure why Cargo will allow you to have version 1.1 and 0.5 at the same time but not 1.1 and 1.0, but my guess is that a heuristic is to have only one semantic major version of a given crate.
Future enhancements to Cargo will likely introduce the concept of "public" and "private" dependencies, which will likely change the resolution algorithm as well as make this case better as byteorder is probably an internal dependency of mysql and you don't need to match it.

Related

what does this mean? "This is precisely because a library should not be deterministically recompiled for all users of the library."

I am new to Rust and is trying to understand the Cargo thing. I read in their FAQ about "why do binaries have Cargo.lock in version control, but not libraries?" but do not understand what this means.
"Users dependent on the library will not inspect the library’s Cargo.lock (even if it exists). This is precisely because a library should not be deterministically recompiled for all users of the library.
If a library ends up being used transitively by several dependencies, it’s likely that just a single copy of the library is desired (based on semver compatibility). If Cargo used all of the dependencies' Cargo.lock files, then multiple copies of the library could be used, and perhaps even a version conflict."
Appreciate if anyone can explain this. Thanks.
Say we have the following crates:
string-tools: this is some kind of commonly used library exporting a FastString struct that has faster implementations of some commonly needed functions. This library has two versions: 1.0.1 and 1.0.2. Version 1.0.2 was recently released.
database: a library to interface with your favorite database. It needs to do some string processing, and so it uses the string-tools library. One of the public methods in database has a signature like this:
fn get_username(id: u64) -> string_tools::FastString
The library has not had a chance or need to update to version 1.0.2 of string-tools - maybe it's unaffected by any of the bugs that were fixed in the patch. Consequently, it's Cargo.lock pins the version of string-tools to 1.0.1.
client: this library is for interacting with the client. It also depends on string-tools, and has a method like this:
fn show_name(name: string_tools::FastString)
This library is using the most recent version of string-tools, version 1.0.2. That is also the version in its Cargo.lock.
You would like to write a website that makes use of the database and client libraries. When you build your project, Cargo compiles each library's dependencies with the versions that are specified in the Cargo.lock. This means that database uses version 1.0.1 of string-tools and client uses version 1.0.2. But now imagine that you've written some code like this:
client::show_name(database::get_username(id));
This should compile. After all, the get_username function returns a string_tools::FastString, which is accepted by the show_name function. But it doesn't! That's because the FastString returned by get_username comes from version 1.0.1 of string_tools, while show_name wants a FastString from version 1.0.2! This can't work; after all, when the patch to string-tools for version 1.0.2 was written, it's possible that the author added an additional field to the type. There's nothing reasonable for the compiler to do.
This kind of issue is avoided by having Cargo ignore the Cargo.lock file on libraries. Instead what it does is compile both database and client against the 1.0.2 version of string_tools. This is correct because 1.0.1 and 1.0.2 are "semver compatible versions." That means it is ok to change out one for the other, things must still compile. Now you no longer get a compiler error, because the types that the one function returns and the other function accepts are no longer different.

cargo version: What doest the -nightly suffix mean?

I've built a rust toolchain using someone else's repo, and it contains
a configure line I'm not familiar with: --release-channel=nightly
After I build and install the toolchain it reports its version as:
rustc 1.57.0-nightly (f1edd0429 2021-11-29)
while the standard off the shelf install of 1.57 reports its version as:
rustc 1.57.0 (f1edd0429 2021-11-29)
What does the nightly suffix mean in this version string?
Nightly is an unstable, unfinished, experimental version of Rust.
The name comes from the fact that literally every night a new "nightly" version is automatically released from whatever happens to be in the rust-lang repository at the time.
The nightly version allows use of Rust features that are still work in progress, special unstable features that are private to the compiler or the standard library, or even features that were failed experiments and will never be available in the stable releases of the Rust language.
Nightly versions may crash, may miscompile code, may stop working at any time. There is absolutely no warranty about them. Do not use them unless you specifically want to test the latest, unfinished Rust language features.
There's one case where users may hear about "nightly" features: when they use an outdated version of the compiler.
When an old Rust version sees language features "from the future", it may ask you to enable nightly features to use the code. This is misleading. You should upgrade Rust to the latest stable version instead of using nightly.

Is it possible to specify version for feature in dependency in Cargo.toml?

For example, I use barcoders crate:
barcoders = {version = "0.10.0", features = ["image",]}
Is it possible to specify which version of image this dependency should use?
Something like
barcoders = {version = "0.10.0", features = ["image=0.22.3",]}
Because it uses image crate version 0.18.0 and in my project I use latest 0.22.3.
Does it mean that there's only 2 ways to resolve that:
I downgrade version in my package
Barcoders dependency get updated
No, there is no way to specify the version for a dependency's (optional) dependency. This makes sense, as your dependency run their tests only against the version they specify in their Cargo.toml. In this case, as it appears everything you're doing uses open source, you could fork barcoders, update the dependency, run the test suite and if it passes, use your fork. It would also be polite to open an issue in that case.
If barcoders wasn't open-source, so you couldn't fork it, your best bet would be to switch to the version of image that barcoders uses. If your crate is a library, it may be annoying to expose a public interface that uses outdated libraries, but that's life. The "proper" solution to this problem is to wait until image has a 1.0 release, which is basically a forward compatibility promise, then barcoders can specify image = "^1" (i.e. >=1.0.0 <2.0.0). I mention this "solution" only because you appear to have commit privileges on barcoders, in fact you solved your own problem by updating the image dependency in barcoders.
As one of the comments points out, this version compatibility issue is less fragile that it at first seems. So long as types from different versions of some dependency crate don't cross api boundaries, your project can include any number of versions of that dependency simultaneously. Working with multiple versions of libraries took some work from the rust team on name mangling, which you can read about here
No, you can't, and shouldn't, and shouldn't worry.
Libraries were developed at a single point in time, used dependencies with a certain API. The dependency is likely to change some of that between major versions (changing the type a function returns, exposing different patterns, or whatever). This may make it unable to compile anymore. To really update something, you might need to change parts of the code that is using the dependency in the first place.
This is open source world, so you can do so and publish a pull request in the original crate to update. It might be appreciated, but don't underestimate the care that needs to be taken to not break other people's crates yourself when doing so.
Or make your own fork of the crate that updates it just for you.
But you are probably just worried seeing duplicates of the same crate with different versions during compilation. Cargo indeed compiles with different versions, so all calls to the dependended crate will receive what the developer expected when he/she wrote it. This is not a problem, in performance, or amount of instructions that end up in the binary. Just stop worrying.

Rocket requires a minimum version of Rust nightly, but a higher stable version is already installed

I'm trying to run Rocket but I'm falling at the first hurdle. When trying to cargo run, I get the following error:
error: failed to run custom build command for `pear_codegen v0.1.2`
Error: Pear requires a nightly or dev version of Rust.
Installed version is: 1.33.0 (2019-02-28). Minimum required: 1.31.0-nightly (2018-10-05).
I'm new to Rust, but coming from other languages this makes no sense whatsoever. It needs version 1.31.0 as a minimum but I have version 1.33.0 installed.
What am I doing wrong?
If software requires a nightly build of Rust, no stable version of Rust can be substituted: you are required to use nightly.
The nightly channel of Rust is a superset of stable Rust. Features that are not yet complete or simply haven't proven their value are included in nightly builds of Rust. You opt into using a given feature via a crate attribute.
These unstable features may completely change or even be removed at any time. Said another way, an unstable feature is never guaranteed to exist in any particular Rust stable version.
If it helps, you can think of nightly versions as an "alternate reality" track of development. The version number of nightly is only a loose indicator of where they exist in time; the compilation date and git commit hash are much more informative.
I would have thought the nightly code from 1.31.0 would be pushed into the stable 1.31.0+ versions once tested
This is how the beta channel works — anything in 1.x.y-beta will be in 1.x.y-stable (assuming no major emergency occurs).
See also:
What is the stabilization process?
error[E0554]: #![feature] may not be used on the stable release channel Couldn't install racer using cargo
What is a crate attribute and where do I add it?
You aren't doing anything wrong, Rocket just requires Nightly builds so it has access to newer features of Rust that might've not stabilized yet.
You can opt to only use a Nightly build for your Rocket project, per the documentation:
rustup override set nightly
Getting started guide

Is it possible to run cargo install with a specific date instead of version numbers?

I want to install a package and all its dependencies as they were at a specific date and time in the past.
I need to use a slightly older version of rustc-nightly, and therefore I need to make sure that all dependencies pulled by cargo install compile against that old version of the compiler.
Currently, when I specify the version of the top-level package to install, it still seems to pull the latest version of some dependencies, which don't build with the old compiler.
No, this is not possible.
Your best options are:
Upgrade the compiler. If you "can't" do this, evaluate why you can't and decide how much benefit you are getting from that.
Add the dependencies to your own Cargo.toml pinned to an older version that does work.
You can try forking the crate index and rolling it back, but there's no guarantee that will work either.
seems to pull the latest version of some dependencies
Yes, most libraries specify dependencies with a semver-compatible range, such as my-library = "1.0". This will allow any version from 1.0.0 to 1.x.y.
Unfortunately, there's no consensus on whether requiring a new version of Rust constitutes a semver-breaking change yet.
See also:
What exactly is considered a breaking change to a library crate?
How to generate Cargo.lock based on last month's crates.io?

Resources