pre-RFC: LTS for the Rust Toolchain

Completely false. In a shared codebase, any one individual may be able to pull in new dependencies or write code using a new feature, but updating the MSRV requires the agreement of everyone working on that codebase.

That's why the Linux kernel has an MSRV of 1.85 and will continue to do so for maybe 2 more years.

I don't think you get to expect to download new versions of a dependency in that situation. It may work, but you don't get to complain if some dependency since bumped MSRV. If you want things to work you should stick to "period accurate" dependencies.

I was arguing against the "nobody has this problem" argument.

The toolchain absolutely is more likely to be difficult to update than almost any other library.

I have no count on the number of times I've seen the rustc update get delayed at work. There are all sorts of issues that could come up. The linker fails to find some random symbol? Something changed in the build and our non-cargo build system needs to be changed? Some issue with the clang compiler, which must match rustc's LLVM to do LTO?

And of course, there are also the new lints that get added every new release.

To be clear, I don't think rustc having an LTS would help us. We would still be doing our best to catch up with the latest rustc because there are many benefits to do so, and the effort to stay up to date would be worth it.

But don't say it's easy to update for everyone because it's not.

5 Likes

Do you mean you're using deny(warnings) as CI-blocking check? If so, for this one point imo you don't get to claim that rustc makes it difficult to update. This is self-imposed and there are other ways to do that which would decouple upgrade decisions from cleaning up lints.

2 Likes

Of course we clean warnings in our codebase? It's downright bad engineering to have lots of warnings emitted in your build.

As you say, lints can be silenced, so they aren't something that blocks an upgrade for a long time (those are the other things I mentioned). Yet they are still part of the reason why "upgrade rustc" is someone's job.

1 Like

During an upgrade it makes sense to not deny new warnings. This is how we do it at work for C++: during an upgrade we quickly look at the new warnings and there are many of a type or it would take more than a moment to fix we allow those warnings and create cases on fixing them, which will happen over the next several weeks. (We don't yet have enough Rust for that process to be difficult, the Rust code base is small enough that it takes at most a handful of minutes to fix warnings.)

But I think that is a pretty standard way of doing things.

I mean that sounds pretty similar to us. Either we fix the warnings right away, or we add #[allow()] statements to silence them and file bugs for follow-up.

But we don't remove -Dwarnings.

Since I let rustc auto-update I don't block CI on warnings directly, to me they're more of a maintenance task like bumping dependencies or deleting obsolete code. In a way new warnings introduced by a rustc update are an issue that was always there and only now got discovered, just like a bug in a dependency.

There's a code quality tool that reports the deltas for PRs, if a dev makes changes that result in additional warnings, then those do get reported.

1 Like

You all are focusing too much on the lints part. The bigger problem is:

1 Like

Yeah valid. You say LTS wouldn't help... is there anything that would? E.g. the clang/llvm part, rustc already should support going back to N-1, are you using that already, if not is it too unreliable or ...?

I imagine the build std effort will improve at least some of the things that cause us issues. But I'm not sure on the details because I'm not the one doing these updates. I just hear about them from the toolchain team.

I've long proposed (but haven't led any efforts) to add metadata to our lints in rustc and clippy with "introduced" and "last significantly modified", so that then tooling could either use the MSRV information or (preferably) a new field stating "I care about lints up to 1.XX version". This way you can assert in CI that no lints contemporaneous to the oldest toolchain devs use get triggered in CI, where presumably updating to the latest stable version might be more useful than for individual devs. Having this would separate the "update rust-toolchain.toml" PR from the "address new lints" PR in every project I have experience with.

9 Likes

I do think it'd be helpful to have that available as a tool. But also, I think it's a feature to combine "bump toolchain version" with "fix new lints", rather than leaving them temporarily disabled.

This conversation seems to continue to be me and everyone else talking past each other so this is going to be my last word on the subject until I have time to write up the “making it easier for everyone to be flexible about their dependencies” RFC that I mentioned earlier. I’m only going to respond to some meta-level issues right now:

That is not what I’m asking for; in fact, I’m convinced that if we fully adopted and embraced the changes I have in mind, in the long run it would make the language less stagnant. But I’d prefer to suspend that conversation until when I have a pre-RFC to point at.

I’m just going to observe that I’m starting from very different premises than you are, here: I think it’s bad that there’s only one (up-to-date) implementation of Rust, and I think it’s bad when people write code, intended for other people to use, that insists on very specific versions of any of its dependencies (including the language). I think those things are worse than the additional maintenance or legibility burdens you’re so worried about. I think they cause more problems for more people.

I also think we can have the best of both worlds. I think we can design better metaprogramming tools than #ifdef, and I think there might well be less resistance to tracking the latest stable compiler if those tools existed. But, again, let's save that for the pre-RFC thread.

Yes, it’s irrational, or more precisely pre-rational. Reason does not come into play. Kahneman’s System 1 says “we’ve had tedious, unpleasant experiences with software updates in the past, we anticipate the same process being tedious and unpleasant for Rust language updates.” And the thing about System 1 is, it doesn’t listen to reason. You cannot talk people out of System 1 anticipations with reason alone, nor with personal experiences.

The short version of what does work is, first make the person who you think is being “irrational” about something feel fully in control, not under time pressure, not in danger of losing anything important if they take a risk; then let them encounter the situation that they’re avoiding on their terms and have a more positive experience with it.

Thus my core recommendation of making much heavier use of #[feature] so that people know, in advance, that updating the Rust compiler won’t change the language it accepts until they start flipping clearly marked switches.

Relatedly:

Your reaction sounds to me like you haven’t ever actually had to calm down a child who’s panicking about imaginary monsters? Yes, in the long run the goal is to teach the child that there are no monsters, but in the moment, System 1 won’t be persuaded.

The neat thing about putting a tool in the child’s hands that they can use to make the monster go away is, the child feels reassured of their safety in the moment, and over the course of the next several days the child is likely to notice that they have never needed to use the tool, which makes them more receptive to the idea that maybe there aren’t any monsters to worry about.

I suspect you don’t realize you’re doing this, but the way you phrased your disagreement here is extremely rude. I said “I think the language would be better if we did X” and you replied “I don’t think we should make the language worse by doing X”. That turns “the language would be worse if X” into a presupposition: you are asserting that it is settled that the language would be worse if X, and anyone who claims otherwise, i.e. me, is arguing in bad faith.

I am not arguing in bad faith. I honestly believe that the language would be better, for everyone, including you, with a slower release cadence (and with a bunch of additional features and altered community norms etc etc). You don’t have to agree with me, but grant me the damn courtesy of believing that I believe this!

I say it is. I say that’s how much time it actually takes me on my projects. You say it takes you less time, OK, but now we’re dueling anecdotes again. How about we get some funding to do some actual time and motion studies here?

1 Like

This sounds like a good premise to justify in the RFC, because it's not at all obvious to me.

Usually I see that as an inherited wish from C++ experience of vendor lock-in that comes from an era before free and forkable open source compilers became the norm. I don't think I've ever seen an argument I found persuasive for why people actively using as their normal development and shipping target anything other than the normal rustc is better.

(That distinguishes things like "mrustc for bootstrap" or "different grammar specification for fuzzing against rustc" that I do agree are at least potentially interesting.)

7 Likes

I observe, from your comments earlier in the thread, that your expectation of the value provided by newer language versions/features is very different than others:

Suppose Rust comes out with a new language feature tomorrow, that I want to use in my crate. Today, I use that feature and update my MSRV accordingly. In the world you describe, I wait ten years before using that feature, because there's no point in using that feature if I have to provide an alternate path that works under older compilers, because I don't want to maintain two paths. This is why people are describing the world you want as evoking "stagnation".

If, in the world you're describing, there is a way to use that feature immediately, without writing and maintaining an alternate code path, I'd love to hear it. If there isn't a way, then that is one major reason I don't want to see that world inflicted on the ecosystem.

Tangent: discussion of one option I've seen for that, which doesn't seem viable

The only serious proposal I've ever seen for that is "what if we have an official tool for desugaring new features into old code". And then that tool is another dependency with version requirements. Also, that only works for features that can be compatibly desugared into old code.

4 Likes

Let's make the value discussion a bit more concrete with things I'm aware of that will immediately benefit foundational crates that require MSRV bumps.

Say a new Rust/Cargo version comes out tomorrow that

  • Reduce 90% of build scripts for packages that use new features
  • Speed up the build for sys crates that opt-in, by building the C library in parallel, only blocking a link job
  • Demonstrate that build scripts or proc-macros that opt-in aren't doing anything that needs further auditing and are more caching friendly
  • Allowed rewriting proc-macros into other mechanisms that didn't require compiling + linking + execution

How long should it take before foundation crates, the ones that would most benefit from these changes, update their MSRV to include it? Now if these are staggered across multiple releases?

Those are all things we are looking at improving but each one will require an MSRV bump when adopted.

6 Likes

To add to that -- this world makes it much harder to become motivated to work on new features, if it takes that long before they have measurable benefit.

As a long-term community member I am used to playing the "long game" in Rust, some of my efforts span many years. I have seen many proposals crop up and whither from not having someone to consistently push for them over the span of years. But if I imagine having to wait another 10 years after the feature is finally stable... that's demotivating.

So I think a lot of the stagnation here would be from the second-order effect of making it less rewarding to work on new features. People paid to work on Rust would of course continue to do so, but the stream of free-time contributions from people doing it for the love and the fun would greatly diminish, and I think that would be a big loss.

Now, to be fair, C++ is evolving despite their long feature cycles. My hypothesis here is that the evolution is mostly pushed by organizations that will then adopt those new features as soon as they are available, and not wait another 10 years. But I don't know if that is actually true.

10 Likes

Maybe this is my brain having been warped by decades of maintaining software written mostly in C, but I do not understand why you wouldn't be happy to write that alternative code path. You write it once, you set up CI to ensure it keeps working when necessary, and then you probably never have to touch it again. If all the conditionally present features are individually detectable and sensibly decoupled from each other (Rust almost has this already), the maintenance burden should be negligible. If you really want, you can schedule deletion of old polyfills once every few edition boundaries, but I don't particularly understand why you would bother; why not keep the old compilers usable forever? What is the gain, besides the warm fuzzies from deleting code?

I do get why you would want to not have to think about the alternative code paths when working on the rest of the program, but with suitable metaprogramming capabilities (again, Rust almost has everything necessary already) the alternative paths can live in submodules devoted to the purpose, and most of the code looks just like it would if you were only supporting the newer compiler.

These all sound big enough, and foundational enough, to actually pay for the negative externalities of an MSRV bump. I'm not saying one should never bump one's MSRV, especially not this decade (where there's a solid case to be made that the language isn't actually "finished"). Just that I think there's a lot we could and should do to make it easy and natural to not bump the MSRV for relatively small things (on the scale of e.g. Error moving to core).