Add guidance about bumping the minimum required rustc
version
#123
Replies: 51 comments
-
I think it's fair and simple to assume any change to the minimum What's less clear is when that happens. If you happen to have a minimum version of So I think a good recommendation is to pin a |
Beta Was this translation helpful? Give feedback.
-
I am less of a hardliner on this, although I hear many people think I am wrong. Serde tracks a Rust version in CI so that we don't accidentally raise the minimum, not because we never will. Users should not have an expectation that they can selectively pin parts of their dependency graph while receiving updates to things that depend on those parts. In Serde's case, the serde and serde_derive crates are versioned in lockstep. Serde 1.0.5 and serde_derive 1.0.5 work together, and serde 1.0.10 and serde_derive 1.0.10 work together. There shouldn't be an expectation that you can pin serde to 1.0.5 and get to use serde_derive 1.0.10. Does that constitute a "breaking change" to serde_derive? Sure, technically. Dependencies on the standard library or language should work analogously. |
Beta Was this translation helpful? Give feedback.
-
I'm not sure I agree that this needs to be treated as a breaking change. On one hand, you have the cost of forcing people to upgrade their rustc when Judging by the recent Rust community survey survey responses, ~95% of respondents were targeting 2 stable releases back or newer. I am looking forward to picking up the |
Beta Was this translation helpful? Give feedback.
-
Usually I would expect bumping the minimum |
Beta Was this translation helpful? Give feedback.
-
I have some thoughts on this, but I think that at a minimum, we should be strongly recommending that folks include a specific version of Rust in their CI builds. This doesn't imply any particular semver policy, but does at least make bumping the minimum Rust version a conscious change. As someone who has tried to play the conservative side of this fence (semver bump for minimum rustc bump), I will say that having a pinned Rust version in a CI config makes the research task of determining the release policy of your dependencies much easier, and typically instantly clarifies the choices I actually have available to me. I've been trying to straddle this line for a long time now myself, and it is very hard to do so. The hardest part about this problem is that there are significant reasons for choosing either side of this, and I think those reasons have already been highlighted in this thread. Another difficult aspect of this problem is that since there is no consensus on whether a minimum rustc version bump is actually a semver breaking change or not, you end up with an ecosystem where one needs to be aware of the release policy for each dependency of your code, because that policy is not necessarily communicated through version numbers. Even worse, this doesn't just apply to direct dependencies, but to all transitive dependencies too. I'd like to collect a few of my experiences with this topic.
I think that's it as far as my own experiences go. It has been a struggle, but sometimes I wonder whether it is a natural struggle or not. This is why I think making the release policy of a crate discoverable is a first good concrete step we can take that will improve the situation for everyone, and I think it should be uncontroversial. (I think a minimal release policy is putting a pinned Rust version in a CI config.) My own personal views on this have historically been a little hardline in the sense that I thought it was a semver breaking change. But I've grown increasingly soft on that particular issue. The end result is that I am intensely ambivalent. Therefore, I wind up on the conservative side of things because I'm not sure what to do. That is, all of my crates are pinned to a specific Rust version and I only ever bump the minimum Rust version with a semver version bump. My conservative stance on this is what has been preventing me from pulling the trigger to release regex 1.0, for example. I really want to use SIMD optimizations in regex, but if I release regex 1.0 now, then under my conservative policy, I wouldn't be able to use SIMD unless I either released regex 2.0 or put the support behind a feature flag that is disabled by default on older Rust versions but enabled otherwise. (At least, I think I could do that.) Either choice is a huge pain and not ideal at all. I suppose we shouldn't necessarily be afraid of making many semver version bumps ("so what if I release regex 2.0"), but every time I do that, some portion of the crate ecosystem is going to build both regex 1 and regex 2 into their crates, which aside from being just plain weird is going to hurt compile times quite a bit! I have been considering what I think of as the clap compromise for regex though, because I also think it's weird to be holding back from regex 1.0 because of this. In particular:
Invariably, this results in the same suggestion that clap makes: if you care about Rust compiler compatibility, then use |
Beta Was this translation helpful? Give feedback.
-
I still wish that a crate's minimum rustc could be expressed to cargo, as well as minimum std and cargo if we don't conflate all of these. Then cargo could just use that for dep-solving, pruning out versions from the registry that are too new for the toolchain in use. That still requires crates to be diligent about declaring their requirement, hopefully with CI to prove it, but it would be better than the status quo. There have been a few RFCs started along these lines, and I admit I just sat back on the assumption that things were headed on the right track. Can anyone summarize what's happened with that? |
Beta Was this translation helpful? Give feedback.
-
@BurntSushi For things like SIMD optimizations that don't affect the public API, there is a third option. You can check the rustc version in a build script and set appropriate CFGs to enable or disable the SIMD codepaths automatically. This does mean that you now have two implementations of a bunch of stuff, but that may already be the case if you're specifically targeting AVX or whatever. |
Beta Was this translation helpful? Give feedback.
-
@cuviper Rough summary: any kind of version specification depends on new features in Cargo and |
Beta Was this translation helpful? Give feedback.
-
Yeah that's what I meant by adding a feature. It's definitely a pain but yeah that might be what I end up doing. |
Beta Was this translation helpful? Give feedback.
-
It does seem like a proper solution to this isn't possible without help from cargo, but it also seems like we can all agree that bumping the minimum rustc version should at least be a minor version bump, and I doubt that's going to change even when cargo has a great, stable solution for this. So for the purposes of the API guidelines, it seems safe to recommend that everyone:
Anything beyond that is probably overkill for a typical crate, but this is enough to make upgrading to a more elaborate policy easy if your crate ever becomes atypically popular, and it's enough to avoid putting an undue research burden on anyone that does need to support specific rustc versions. Actually, I just noticed the current guidelines don't talk about CI or versioning at all. Are those considered out of scope? |
Beta Was this translation helpful? Give feedback.
-
I would personally like it if more crates were willing to release >1.0. In my opinion many crates are afraid to release 1.0 for fear of never being able to go beyond. Much of what is being discussed here could be alleviated if crates were more willing to bump the major. I believe a major hesitation to many popular crates releasing a 1.0 is the fear of "getting something wrong" or not being able to take advantage of rustc advances by not being able to go beyond 1.0 as similarly described by @BurntSushi I'm hoping as the "core" Rust crates start going beyond 1.0, it will become more acceptable, but don't see it happening without the "core" or very popular libraries doing it first as they generally set the tone and guidelines. Personally, I also think the, "Never a Rust 2.0" is partially contributing to this phenomenon. This is by no means a call for crates to quickly release a 1.0 followed immediately by a 2.0. But I do wish more crates were willing to bump the major as issues like what's being discussed evolve naturally. I believe if we had more willingness to bump the major, this would probably a be a non-issue. I'm also just as guilty, as even though clap went 2.0 quite quickly, I've been extremely hesitant to go 3.0 for fear of getting "too far ahead of the norm, and therefore something is wrong" which is admittedly a little silly. As mentioned above, I don't think the For now though, I see the above three bullet points mentioned by @Ixrec as being a better policy than none at all. |
Beta Was this translation helpful? Give feedback.
-
A bump of the required rust version should be treated as a a breaking change of your library in the sense of semver. It breaks the Is it enough if you bump the minor version? No! That means that anybody who has caret requirements anywhere inside their dependency chain might get their If one day we might be getting a "minimum rust version required" field in Cargo.toml, and |
Beta Was this translation helpful? Give feedback.
-
@est31 I strongly disagree with any statements that a bump of required Rust version must always be released as a breaking change. Whatever advice we end up giving here will be primarily a discussion around evaluating tradeoffs, not a hard line. If I have a library that miraculously compiles on Rust 1.3.0 and is used by thousands of downstream crates and I discover a performance problem that would be fixed by using
It is pretty clear to me which of these I would do and lose no sleep over. |
Beta Was this translation helpful? Give feedback.
-
@dtolnay This is an argument of the form "if I remove this public function nothing bad will happen because who really uses it and doing a breaking change would require people to update". I really hate how staying up to date on the current crates.io ecosystem is unusable if you have installed your rustc copy from your distro or are pinning your rust compiler for some other reason. I don't say that people should not rely on Rust If you are on an older compiler you aren't even getting compilation errors like "sorry, but your Rust compiler is too old", the errors are more often of the form "function not found" or "this feature is unstable". |
Beta Was this translation helpful? Give feedback.
-
And libc is literally inside the top two of most directly depended upon crates, so it is an exceptional example. |
Beta Was this translation helpful? Give feedback.
-
This shouldn't work as far as I know because whether a given dependency is enabled or disabled is resolved before build scripts are run. |
Beta Was this translation helpful? Give feedback.
-
Slight modification that no longer requires rust-lang/cargo#5499: we could have 1.x unconditionally depend on 2.0 and add a build script to 2.0 as well, which sets a feature on old compilers that turns the entire 2.0 crate into an empty crate. |
Beta Was this translation helpful? Give feedback.
-
I don't have strong feelings about the semver question here, but as someone maintaining a decent-sized Rust application I frequently run into situations where I update a dependency or add a new dependency and need to bump the minimum Rust version I support (which I test against in CI), but I find it virtually impossible to figure out what the actual required version is! It's easy to see that a PR breaks compiling against the pinned Rust version in CI, and often the error message will even name a specific feature that's unstable in that version, but there's no good way (AFAIK) to figure out what version stabilized that feature. (In other cases the error is not as clear and it's even harder.) |
Beta Was this translation helpful? Give feedback.
-
@luser Yeah, I run into the same problem. This is why I've been advocating that everyone put a specific Rust version in their CI config so that others can go and look at it and know what version the project intends to be able to compile on. I think that is an uncontroversial and unambiguously good step forward, and in particular, does not require everyone to agree on the semver ramifications---folks are free to bump the minimum Rust version as much as they want, they just have to do so explicitly. I think more and more projects are doing this, but unfortunately, many still aren't. At that point, you're right, it's a research task to figure out the minimum Rust version. There's probably a script someone could write that does a binary search for you, but this requires having all intermediate versions of Rust installed. (rustup makes this easy, though, may take up a bit of disk space!) |
Beta Was this translation helpful? Give feedback.
-
(See also japaric/trust#103 about running CI with a rust-toolchain file present) |
Beta Was this translation helpful? Give feedback.
-
See rust-lang/rfcs#2495 for a proposal which will allow authors to specify minimum supported Rust version (MSRV), which in combination with additional constraints in dependency versions resolution algorithm resolution algorithm will make changing MSRV backwards compatible change. |
Beta Was this translation helpful? Give feedback.
-
@luser same here, although I must say that while this is pretty annoying when it happens, it doesn't happen often enough to me to be a continuous annoyance. As in, when it happens, I bisect the version required, and don't have to worry about it again for a couple of months. |
Beta Was this translation helpful? Give feedback.
-
An interesting idea from @Eh2406 for a CI setup that should work today. To test MSRV, one can use
Caveats: |
Beta Was this translation helpful? Give feedback.
-
I'm using latest stable as MSRV for the crates I maintain. Hopefully better tools will appear to handle this gracefully for both crate maintainers and old compiler users. |
Beta Was this translation helpful? Give feedback.
-
TL;DR: Strong opinion; have better error message; not a breaking change Like others said, the main problem is that you don't get a nice error message about it. This would remove all the pain for my personal use of Rust. I don't think this should be considered as a change: rustc is not gcc, we all? use rustup that allow to install and update rust on every common OS. People don't need to wait 1 year a .dep from debian stable to update rustc. I agree some project could have some trouble updating rust, but this should be settled by freezing the specific version of each dependency. Let's imagine ~5% of projects and I think it's much less, would have trouble to keep rustc up-to-date. That mean ~95% of users will "pay" for these ~5%, I don't think it's worth it when there is already an easy solution for these ~5%. (Also, I doubt these ~5% want to have a lot of dependency) Semver is about API, it's doesn't have a way to express compiler version. rustc version is a meta dependency, this is not possible to include it in semver. "edition" in cargo should be enough, but in my opinion, it's clearly a way to hide a Rust 2.0. I mean, maybe I'm totally wrong, but between 2015 and 2018 edition, there is obvious breaking change. Ok, have edition make this smooth but if you try, for example, the diesel get started guide, you will have a cargo.toml with edition = 2018 but the guide didn't expect that, some code in the guide will not compile because diesel example use 2018 edition despite all these changes, I never saw someone complaining about update their code. I always saw, "wow nice this is so much better", I think Rust is too afraid of being seen as an unstable language because of pre-1.0. But Rust dev are in the majority not afraid of little change, on the contrary the community always asks to improve Rust. I agree Rust must not change like pre-1.0 but there is a big difference between change everything every month and change little thing sometime. Anyway, we already have this reputation, people often stick to their first impression. I already see that some big crate live in the past (I don't need to name them and a lot of them use https://github.com/dtolnay/semver-trick) and I hope there will not limit themselves just for few use cases. Of course, I don't encourage to always use the last release, but I think using 3-6 months release is perfectly acceptable, for me these are 3 versions of rustc, stable, beta (who use beta :p) and nightly. And we should have a nice error message that just ask to update rustc. |
Beta Was this translation helpful? Give feedback.
-
Here's a hacky PoC cargo-subcommand to generate a "quick" list of the MSRV by crate version for a particular crate. The idea being maintainers could list this data in their README and allow downstream consumers to make a more informed decision about upgrades and such. |
Beta Was this translation helpful? Give feedback.
-
@kbknapp That's really neat! It'd be nice if there was a way for a crate to document what its MSRV is for each version in such a way that the tool could pick it up. That way, for "cooperating" crates, you wouldn't have to do the full search. |
Beta Was this translation helpful? Give feedback.
-
Crates could specify it in Cargo.toml as package metadata? |
Beta Was this translation helpful? Give feedback.
-
@kbknapp nice tool! Some suggestions:
|
Beta Was this translation helpful? Give feedback.
-
@est31 great suggestions! The tool was meant to be more a proof of concept than a full blown project. If someone wants to foster that command into a real solution I'd be happy to help along where I can and also grant full access. I agree some sort of database would be more usable, even if it was some sort of combination of local/"cooperative" crates instead of a full search. However, that also adds a lot of complexity, and potentially cost to such project. |
Beta Was this translation helpful? Give feedback.
-
As far as I know, this repository does not offer guidelines on when it is appropriate to bump a library crate's minimum required
rustc
version. I've found that authors of widely used and foundational crates do not consider bumping the minimumrustc
version to be a SemVer incompatible or breaking change (1, 2). I think it would be appropriate to document what norms the Rust community expects on this issue, so that deviations from the norm can be fixed without having to re-litigate the meaning of SemVer.My personal opinion on the matter is that bumping the required
rustc
version of a library is a backwards-incompatible SemVer change, since it requires applications depending on the library to follow suit. This change can be every bit as painful for application authors as introducing a breaking API change (or more). The minimumrustc
version is 'viral', and a breaking change by a foundational crates forces all other downstream crates to in turn bump their minimum version. Cargo has good tools for introducing new features which may require a newerrustc
version, while it has relatively poor support for resolvingrustc
version dependencies if the author of the library does not follow semantic versioning. Thus, unless Cargo's version resolution begins taking into account requiredrustc
versions, it's better for library authors to put new code requiring newrustc
versions behind a feature flag.Beta Was this translation helpful? Give feedback.
All reactions