Rust LTS — a DIY solution

I've seen Rust users struggle when trying to use less-than-the-latest compiler version, and subsequently ask for some kind of "long term support" version of the compiler that would be usable for longer than 6 weeks.

The thing is, old Rust compilers actually work fine. The problem is with the crates that don't support the old compilers.

While there could be an LTS edition of the compiler, it would be only an indirect way to pressure crate authors to support old compilers. BUT we don't need this! We can keep crates compatible without asking anyone! With a small trick, it's possible to give old Rust compiler versions ability to use all the crates they can handle, without running into unsupported ones.

The solution is:

  1. Make a fork of the crates-io registry and remove crates/crate versions that aren't compatible with the desired old compiler version.

  2. Make Cargo use the fork instead of the full registry.

The second step is surprisingly simple! It doesn't need any of the new alternative registry machinery. It works since an ancient Rust 1.13! In .cargo/config set:

[source.crates-io]
replace-with = "lts-repo-replacement"

[source.lts-repo-replacement] 
registry = "https://github.com/kornelski/crates.io-index"

And for a proof of concept I've prepared a registry fork with crates compatible with Rust 1.24 (that is old enough to be compatible with oldstable Debian).

As a bonus, I've also yanked defunct crates that were published before Rust 1.0. This makes -Z minimal-versions flag work much more reliably.

To generate the config, and also add a couple of hacky symlinks to make Cargo work faster with it, I've made a tool:

So you can use it by:

cargo install lts
cargo lts "https://github.com/kornelski/crates.io-index"

And a cool thing about it is that once you generate Cargo.lock, you don't even need the custom registry config. It will get the right crates straight from crates-io.

You can verify that the registry fork has the same checksums as the original, and the registry config file points to crates-io, so you're getting the exact same crates (the only difference is you're not seeing the wrong crates).


Current issues/limitations:

  • To verify which crates are compatible I'm using JSON output of cargo check. It's quite useful, because I get precise machine-readable compilation errors. The JSON output was added around 1.22, so I can't easily make a filtered index for older Rust versions (but I have data to make registries for all newer versions)
  • I'm ignoring crates that require nightly features. They could theoretically be supported, but it's whole another dimension to think about.
  • I'm running compatibility testing on Linux, so non-Linux crates are problematic. And so are -sys crates that have unusual dependencies. So I still hope for RFC 2495 to make it easy for me.
  • Crates are generally bad at specifying their true dependency versions. I've found lots of cases where they don't actually work with the versions they say they work, e.g. Cargo.toml says foo = "1.0", but the crate uses functions added in foo 1.2.
  • Very old cargo ignores edition = "2018" and happens to work with some "2018" crates. That complicates my classification :slight_smile:

So if you're using an ancient Rust version, I'd love your feedback. Is this good enough? Are there still some broken crates that slipped through? Any missing?

26 Likes

Nice approach of forking the registry. Few months ago I was exploring the idea of forking cargo instead and letting it use third party information, see my MVP commit here. It needs MSRV version info to be specified in json. Advantage of this approach is that users who haven't opt into the fork will still be able to compile projects that check their Cargo.lock in: only the people who edit/change Cargo.lock will have to use the cargo fork. In general, it's great to see ecosystem experimentation on this. Great job!

1 Like

One challenge might be to properly handle versions with security vulnerabilities.

Maybe cargo has explicit handling for that already? For example, if cargo already allows marking a version vulnerable and downgrades to an earlier version (if the newest version available in your forked registry is vulnerable), then this may already be taken care of.

It's possible to yank the vulnerable version, but AFAIK in this case cargo will upgrade, not downgrade.