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

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?

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.

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.

Use exact version numbers in package.json or not?

Common practice for version numbers of npm dependencies in package.json has been to enter exact version numbers (like 1.2.4) instead of inexact version numbers (like ^1.2.4 which allows installing bug fix releases like 1.2.5) to make sure a future installation will not break due to changes in dependencies (see for example this article).
Using exact version numbers has a drawback in that you can't automatically update bug fix versions of dependencies. This is an issue when it's nested dependencies having security fixes or bug fixes. For example, at this moment the package karma-browserstack-launcher uses browserstack, which is using an outdated version of https-proxy-agent containing a security vulnerability. This becomes very visible right now thanks to npm audit which looks for security issues in dependencies.
Since some time we have package-lock.json, which is used to lock down the version numbers of all dependencies. This may change the way we deal exact or inexact version numbers in package.json.
My question is: given package.json and package-lock.json, what is the best strategy nowadays to deal with version numbers of dependencies? Use exact versions or not? How can I deal with security issues in nested dependencies if they don't get upgraded?
My feeling is that
packages that are libraries and meant to be used to others should have inexact version numbers and should specify the minimum they require in order to work; and
top-level projects that aren't going to be included elsewhere should specify the full version numbers of their requirements, so they can have the most control over when things are updated.

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

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.

Check for unwanted wildcard dependencies recursively

I have a library and want to make sure that none of the dependencies in my dependency-tree uses wildcard dependencies. Wildcard dependencies are evil :(
Can I check this recursively with cargo in the command line? Or can I check it manually in Cargo.lock?
EDIT: While crates.io rejects crates with wildcard dependencies since the release Rust 1.6 (approximately, thanks Steve Klabnik), there are still old crates on crates.io that do have wildcard dependencies. I can upload my own crate that depends on such an old crate. Therefore my crate indirectly also depends on wildcard dependencies. This is what I want to avoid and check for.
I have written a small script that takes the crates.io-index and reads the current information for all packages. If a package has a direct wildcard dependency on another package there is a line printed in the format crate-name -> wildcard-dependency, another-one.
There are 995 current packages with wildcard dependencies. The list is exhaustive and will only decrease in length if the packages are updated.
You can go manually through all dependencies in your Cargo.lock and check if they are mentioned in the list. Note that you must use the most current version of your dependencies to assure that you are not depending on an outdated package with wildcard dependencies.
As of a few months back, Cargo no longer allows for wildcard dependencies. So you don't need to do this yourself.
This is a crates.io feature, not a Rust or Cargo feature, so it's not associated with any particular version of these tools. The update happened around the time we released Rust 1.6.

Resources