So uh. For /reasons/ that I might expand on in future blog posts1 I needed to work on an old (~4y old) library… Without a commited lock file… With some dependencies being yanked… And some dependencies requiring much newer MSRV

How it started:

  1. {library} depends on ahash ^0.7.62
  2. ahash >= 0.6.0, <=0.7.6 is yanked
  3. ahash ^0.7.7 has MSRV 1.60.0
  4. {library} has MSRV 1.59.03
  5. uhhhhhhmmmmmmmmmm

So, I can’t update ahash to a newer version, because it would not be supported by rust 1.59.0. And I can’t keep it at 0.7.6, because the MSRV CI will not pass and thus I can’t merge the PR…

🐸

can’t you just raise the MSRV?

I- look-

🐸

uhuh?

Yes, I /could/4 but a) I didn’t think of that originally b) I never search for easy ways out c) it’s nice to keep MSRV as is d) :(

Anyway-

What I’m looking for is a way to get a Cargo.lock file which would

  • contain ahash =0.7.6 (so that cargo actually allows me to get it), and
  • only contain versions of libraries with MSRV <= 1.59.0

My first idea was to generate a lock file with a new version of cargo, and then manually downgrade ahash to 0.7.6. At first I couldn’t find how to downgrade things with cargo at all, but then a friend of mine, Jonathan Brouwer5, helped me find that the way to downgrade a dependency is cargo update {dependency} --precise {version}6.

However… That did not help much…

Atuin command run history. Every of the 36 commands is in the form of `rustup run nightly cargo update --precise` and then a version and a name of a crate. Some crates like regex and serde_json are repeated multiple times.

(this is not even a half of ithn…)

There are two problems with this approach:

  • There are /so/ many dependencies that need to be downgraded
  • Some dependencies are interconnected and hard to downgrade
    • Crates with exact dependencies, like serde x.y.z depending on serde_code =x.y.z are /especially/ annoying
    • But even just “downgrading A requires downgrading B first because B depends on newer A than I want to downgrade to” can be tiring for popular crates

This is just completely impractical and I had to give up on this idea7.


When my idea did not work, I went to the rust community discord and asked how I could generate the lock file. To my surprise I got a response the same day from synapturanus zombie 𓆏 (frog), who recently helped someone else with a similar issue.

All that’s needed to generate an MSRV-compatible Cargo.lock is

  1. Make sure package.rust-version is set in the Cargo.toml to the MSRV
  2. Remove existing Cargo.lock (if it exists)
  3. CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS="fallback" cargo +nightly check
    • You can also set resolver.incompatible-rust-version = "fallback" in the cargo config, but that’s mildly more annoying
  4. profit!

It’s that shrimple folks!

One caveat is that for this to work your dependencies need to set rust-version too. And since it only got introduced in Rust 1.56.0, this might not work very well for 1.56.0 and a few succeeding versions. Still, you can use this method as a start, and then downgrade the dependencies that don’t accurately set rust-version. As a data point, this method worked for me successfully on 1.65.0, but not on 1.59.0.

Moral of the story: commit your lock files. Also, asking for help leads to being helped. Crazy how that happens.

Thanks again to synapturanus zombie 𓆏 (frog) for helping with this; and thank you to Jonathan and Arya for reviewing this post <3


okmeowbye :3


  1. me when i lie

  2. see The Cargo Book: Version requirement syntax

  3. yes, it’s off by 1 version .-.

  4. and i actually ended up raising the MSRV in a case, before i found other ways to solve this

  5. nya :3

  6. 🐸

    downgrading a dependency requires using update subcommand?..

    shush

    it makes sense, why would have a separate command for that?

    also how are you in the footnotes??

    🐸

    :)

  7. turns out i make a pretty bad dependency resolver