-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Minimum Supported Rust Version #2495
Changes from 3 commits
800825c
4ec9682
f9057da
a43e9e1
00a3022
1e37f4c
a6e42da
e4c638e
7580c20
1aa6476
84aecf1
a78e4b1
73bdc96
7657ef4
ad12112
1a186ea
0cacbb6
386fd8a
33ea5a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
- Feature Name: min_rust_version | ||
- Start Date: 2018-06-28 | ||
- RFC PR: (leave this empty) | ||
- Rust Issue: (leave this empty) | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
Add `rust` field to the package section of `Cargo.toml` which will be used to | ||
specify crate's Minimum Supported Rust Version (MSRV): | ||
```toml | ||
[package] | ||
name = "foo" | ||
version = "0.1.0" | ||
rust = "1.30" | ||
``` | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
Currently crates have no way to formally specify MSRV. As a result users can't | ||
check if crate can be built on their toolchain without building it. It also | ||
leads to the debate on how to handle crate version change on bumping MSRV, | ||
conservative approach is to consider such changes as breaking ones, which can | ||
hinder adoption of new features across ecosystem or result in version number | ||
inflation, which makes it harder to keep downstream crates up-to-date. More | ||
relaxed approach on another hand can result in broken crates for user of older | ||
compiler versions. | ||
|
||
# Guide-level explanation | ||
[guide-level-explanation]: #guide-level-explanation | ||
|
||
`cargo init` will automatically create `Cargo.toml` with `rust` field equal to | ||
`rust="stable"` or `rust="nightly"` depending on the currently used toolcahin. | ||
On `cargo publish` cargo will take currently used Rust compiler version and | ||
will insert it before uploading the crate. In other words localy your `Cargo.toml` | ||
willl still have `rust="stable"`, but version sent to crates.io will have | ||
`rust="1.30"` if you've used Rust 1.30. This version will be used to determine if | ||
crate can be used with the crate user's toolchain and to select appropriate | ||
dependency versions. In case if you have `rust="stable"`, but execute | ||
`cargo publish` with Nightly toolcahin you will get an error. | ||
|
||
If you are sure that your crate supports older Rust versions (e.g. by using CI | ||
testing) you can change `rust` field accordingly. On `cargo publish` it will be | ||
checked that crate indeed can be built with the specified version. | ||
|
||
For example, lets imagine that your crate depends on crate `foo` with 10 published | ||
versions from `0.1.0` to `0.1.10`, in versions from `0.1.0` to `0.1.5` `rust` | ||
field in the `Cargo.toml` sent to crates.io equals to "1.30" and for others to | ||
"1.40". Now if you'll build your project with Rust 1.33 `cargo` will select | ||
`foo v0.1.5`, and `foo v0.1.10` if you'll build your project with Rust 1.30 or | ||
later. But if you'll try to build your project with Rust 1.29 cargo will issue an | ||
error. Although this check can be disabled with `--no-rust-check` option. | ||
|
||
`rust` field should respect the following minimal requirements: | ||
- value should be equal to "stable", "nightly" or to a version in semver format | ||
("1.50" is a valid value and implies "1.50.0") | ||
- version should not be bigger than the current stable toolchain | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean the current nightly toolchain? Because during the lifetime of Rust There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I meant stable. If we have stable Rust 1.35 and nightly 1.37, using |
||
- version should not be smaller than 1.27 (version in which `package.rust` field | ||
became a warning instead of an error) | ||
|
||
`rust` will be a required field. For crates uploaded before introduction of this | ||
feature 2015 edition crates will imply `rust="1.0"` and 2018 edition will imply | ||
`rust = "1.30"`. | ||
|
||
It will be an error to use `rust="1.27"` and `edition="2018"`, but `rust="1.40"` | ||
and `edition="2015"` is a valid combination. | ||
|
||
# Reference-level explanation | ||
[reference-level-explanation]: #reference-level-explanation | ||
|
||
The describe functionality can be introduced in several stages: | ||
|
||
|
||
## First stage: dumb field | ||
|
||
At first the `rust` field can be simply a declarative optional field without any | ||
functionality behind it. The reason for it is to reduce implementation cost of | ||
the first stage to the minimum and ideally ship it as part of Rust 2018. | ||
It will also allow crate authors who care about MSRV to start mark their crates | ||
early. | ||
|
||
|
||
## Second stage: `cargo publish` check | ||
|
||
The next step is for `cargo publish` to require use of the toolchain specified | ||
in the `rust` field, for example crates with: | ||
- `rust="stable"` can be published only with a stable toolchain, though not | ||
necessarily with the latest one. Cargo will insert toolchain version before | ||
publishing the crate as was described in the "guide-level explanation". | ||
- `rust="nightly"` can be published only with a nightly toolchain. If finer | ||
grained "nightly: ..." (see "nightly versions" section) is selected, then one | ||
of the selected Nightly versions will have to be used. | ||
- `rust="1.30"` can be published only with (stable) Rust 1.30, even if it's | ||
not the latest stable Rust version. | ||
|
||
Using the usual build check `cargo publish` will verify that crate indeed can be | ||
built using specified MSRV. This check can be used with exisiting `--no-verify` | ||
option. | ||
|
||
## Third stage: versions resolution | ||
|
||
`rust` field becomes required and cargo will add it as a constraint to dependency | ||
versions resolution. If user uses e.g. Rust 1.40 and uses crate `foo = "0.2"`, but | ||
all selected versions of `foo` specify MSRV e.g. equal 1.41 or bigger (or even | ||
nightly) `cargo` will issue an error. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this mean in this context: If
I would want to make sure your crate fails to build in this case because otherwise you might be using an insecure or error-prone version of a crate in this case even if the issue was patched. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should be a separate flag to indicate insecure versions, a bump in patch level is meaningless for determining if there are open security issues. Security flaws can be discovered, but not fixed, so reporting error-prone versions should be separate from uploading new versions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you write There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's explicitly not how it works.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is already precedent for not always pulling the latest, wouldn't rustc needing version X be akin to the create's needs? Not that I need any specific behavior... I join this thread when I discovered that I couldn't build RLS on Debian's rustc and the failure mode was to try to build and fail. I'm most interested in the cases where rust does build something, but doesn't produce well and good binaries because it's lacking some feature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You already can (and probably should) yank insecure crate versions. There is also @mooman219 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL it doesn't use the latest version if you don't specifically tell it to. My apologies! |
||
|
||
`rust` field value will be checked as well, on crate build `cargo` will check if | ||
all upstream dependencies can be built with the specified MSRV. (i.e. it will | ||
check if there is exists solution for given crates and Rust versions constraints) | ||
|
||
Yanked crates will be ignored in this process. | ||
|
||
Implementing this functionality hopefully will allow to close the debate regarding | ||
MSRV handling in crate versions and will allow crate authors to feel less | ||
restrictive about bumping their crate's MSRV. (though it can be a usefull | ||
convention for post-1.0 crates to bump minor version on MSRV change to allow | ||
publishing backports which fix serious issues using patch version) | ||
|
||
## Extension: nightly versions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the idea of flagging supported compiler versions, but I don't quite understand how a crate would actually be able to use this in practice if the list of supported versions has to go in the published Cargo.toml file itself. Typically when publishing a nightly only crate, I know it works for the current nightly and that it will continue to work for all future nightlies until there is a breaking change. With this design, it seems like I'd need to publish a new version of the crate every day with an updated Cargo.toml saying that the new nightly also works or else my users would be unable to upgrade their compiler. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I see it, the main use-case will be to provide exact nightly version, on which crate is developed and tested. So e.g. if you depend on If your crate does not experience breakage too often, then you can use default There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the field to indicate a known good nightly version seems reasonable. However, I'd be a concerned about it factoring into version resolution then ("stage 3" of the RFC). The entire ecosystem of nightly crates would have to update in lockstep or else become mutually incompatible. Further, trying out new nightlies would require waiting for all your dependencies to be updated. Basically, you'd no longer be running on the nightly channel but instead a sort of "monthly" channel which lacked both the stability of stable and the quick bug fixes of nightly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think most of the nightly crates will just use "nightly: *", some may use ranges (though properly testing it will not be easy), finer grained nightly selection will be used only be a handful of crates with rare intersections with each other. Plus do not forget that you always can disable MSRV constraint in dependency versions resolution with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sorry, I'm still not fully understanding. Lets use a concrete example. I maintain the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem with "all future nightlies until another breaking change happens" is that this constraint can be properly expressed at the moment of crate publishing, as you don't know when breaking change will happen. The closest thing which we can get is to use But this approach feels quite fragile. To reliably solve this problem we will need an additional channel for notifying users "this crate broke on the following nightly versions" which can't be done via |
||
|
||
For some bleeding-edge crates which experience frequent breaks on Nightly updates | ||
(e.g. `rocket`) it can be useful to specify exact Nightly version(s) on which | ||
crate can be built. One way to achieve this is by using the following syntax: | ||
- single version: rust = "nightly: 2018-01-01" | ||
- enumeration: "nightly: 2018-01-01, 2018-01-15" | ||
- (inclusive) range: "nightly: 2018-01-01..2018-01-15" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
or
(every new nightly is considered a major version for sure.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not particularly attached to the proposed syntax (I think that exact versions ("nightly: 2018-01-01") will be used much more often than other variants), so probably using semver operators will be indeed a better choice. Though, after some thought I've started to worry about inconsistency between how "stable" and "nightly" work. I think it will be better to make "nightly" work in the same fashion as "stable", i.e. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good to me, though it makes me think if it makes sense to mix both stable and nightly in the same line?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, any use-case examples? As I see it, if code enables nightly features it can't work on stable, and if it works on stable it should work on nightly. Well, excluding potential nightly regressions of course, which I think we can safely ignore. UPD: Also there is a very old nightly versions possibility, but I think we can ignore such cases as well. Though from consistency point of view, such pattern could be allowed, but I am not sure if it's worth the additional complexity. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @newpavlov |
||
- enumeration+range: "nightly: 2018-01-01, 2018-01-08..2018-01-15" | ||
|
||
Such restrictions can be quite severe, but hopefully this functionality will be | ||
used only by handful of crates. | ||
|
||
## Extension: cfg based MSRV | ||
|
||
Some crates can have different MSRVs depending on target architecture or enabled | ||
features. In such cases it can be usefull to extend `rust` field, e.g. in the | ||
following way: | ||
```toml | ||
rust = "1.30" | ||
rust-cases = [ | ||
{ cfg = "x86_64-pc-windows-gnu", version = "1.35" }, | ||
{ cfg = 'cfg(feature = "foo")', version = "1.33" }, | ||
] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer we reuse the existing rust = "1.30"
[target.x86_64-pc-windows-gnu]
rust = "1.35"
[target.'cfg(feature = "foo")']
rust = "1.33" (Note that cargo should either fully support There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, reusing As for |
||
``` | ||
|
||
Version resolution will filter all cases with `cfg` equal to true and will take | ||
max `version` value from them as a MSRV. If all `cfg`s are false, value in the | ||
`rust` field will be used. | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
- Declaration of MSRV, even with the checks, does not guarantee that crate | ||
will work correctly on the specified MSRV, only appropriate CI testing can do that. | ||
- More complex dependency versions resolution algorithm. | ||
- MSRV selected by `cargo publish` with `rust = "stable"` can be too | ||
conservative. | ||
|
||
# Rationale and Alternatives | ||
[alternatives]: #alternatives | ||
|
||
- Automatically calculate MSRV. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems too vague to have much meaning; there are wildly different things it could mean, with completely different trade-offs. What is automatically calculating the MSRV? rustc, cargo, crates.io? How is it conveyed? Would it play well with the future work of MSRV influencing version resolution? I can think of two radically different approaches to this off the top of my head, one of which is completely different from this proposal and one of which boils down to just augmenting the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't want to cover all potential approaches, as you do say yourself there are a lot of options to choose from. The main idea here is that instead of asking people to manually select MSRV via the |
||
- Do nothing and rely on [LTS releases](https://github.com/rust-lang/rfcs/pull/2483) | ||
for bumping crate MSRVs. | ||
newpavlov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Prior art | ||
[prior-art]: #prior-art | ||
|
||
Previous proposals: | ||
- [RFC 1707](https://github.com/rust-lang/rfcs/pull/1707) | ||
- [RFC 1709](https://github.com/rust-lang/rfcs/pull/1709) | ||
- [RFC 1953](https://github.com/rust-lang/rfcs/pull/1953) | ||
- [RFC 2182](https://github.com/rust-lang/rfcs/pull/2128) (arguably this one got off-track) | ||
|
||
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
- Name bike-shedding: `rust` vs `rustc` vs `min-rust-version` | ||
- Additional checks? | ||
- Better description of versions resolution algorithm. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be "
foo v0.1.10
if you build your project with Rust 1.40 or later" (1.30 -> 1.40)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you are right. Thank you for noticing the typo!