-
Notifications
You must be signed in to change notification settings - Fork 1.6k
RFC: Minimum Supported Rust Version #2495
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
Changes from 4 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,198 @@ | ||
| - 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. If "nightly: \*" is used, then `cargo` | ||
| will not select current Nightly version, but will assume that cratecan be built | ||
| with all Nightly versions. | ||
|
|
||
| In case if you have `rust="stable"`, but execute `cargo publish` with Nightly | ||
| toolcahin you will get an error. Same goes for `rust="nightly: *"` which can be | ||
| published only using nightly toolchain. | ||
|
|
||
| If you are sure that your crate supports older Rust versions (e.g. by using CI | ||
| testing) you can specify this version explicitly, e.g. `rust="1.30"`. | ||
| On `cargo publish` it will be checked that crate indeed can be built with the | ||
| specified version, i.e. the respective toolchain will have to be installed on | ||
| your computer. | ||
|
|
||
| The value of `rust` field (explicit or autmatically selected by `cargo`) will | ||
| be used to determine if crate can be used with the crate user's toolchain and | ||
| to select appropriate dependency versions. | ||
|
|
||
| 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. | ||
| Note that "1.50" is a valid value and implies "1.50.0". (also see "nightly versions" | ||
| extension) | ||
| - version should not be bigger than the current stable toolchain | ||
|
||
| - 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 with minimal checks. 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 will be used as a constraint for 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. | ||
|
||
|
|
||
| `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 | ||
|
||
|
|
||
| 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: | ||
| - auto-select: "nightly" This variant will behave in the same way as "stable", i.e. | ||
| it will take a current nightly version and will use it in a "more or equal" constraint. | ||
| - single version: "nightly: 2018-01-01" (tha main variant) | ||
| - enumeration: "nightly: 2018-01-01, 2018-01-15" | ||
| - semver-like conditions: "nightly: >=2018-01-01", "nightly: >=2018-01-01, <=2018-01-15", | ||
| "nightly: >=2018-01-01, <=2018-01-15, 2018-01-20". (the latter is interpreted as | ||
| "(version >= 2018-01-01 && version <= 2018-01-20) || version == 2018-01-20") | ||
|
|
||
| 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 describe how MSRV depends on them, | ||
newpavlov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| e.g. in the following way: | ||
| ```toml | ||
| [package] | ||
| rust = "1.30" | ||
|
|
||
| [target.x86_64-pc-windows-gnu] | ||
newpavlov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| rust = "1.35" | ||
|
|
||
| [target.'cfg(feature = "foo")'] | ||
newpavlov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| rust = "1.33" | ||
| ``` | ||
|
|
||
| All `rust` values in the `target` sections should be equal or bigger to a `rust` value | ||
| specified in the `package` section. | ||
|
|
||
| If target condition is true, then `cargo ` will use `rust` value from this section. | ||
| If several target section conditions are true, then maximum value 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. | ||
|
Member
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
Contributor
Author
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. | ||
| - How nightly versions will work with "cfg based MSRV"? | ||
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.
I don't like this kind of automatic conversion from
stableto e.g.1.30. I think it is counter-intuitive.Instead, I would like to propose a slightly more complicated approach with a huge effect on the question raised above (namely "Is a MSRV change a breaking change for a crate which then requires a major version bump?"). Let me give an example:
A user might declare the MSRV to be
1.30. This means that the crate which is published to crates.io can be compiled by every compiler between1.30(inclusive) and2(exclusive), just as semantic versioning suggests. If he uploads the next (patch or minor) version of the same crate, the MSRV must be equal to or smaller than1.30. Everything else would be considered a breaking change. If he uploads the next major version, of course he's free to change the MSRV.A user might also declare the MSRV to be
stable. When published to crates.io,stableis converted tostable(1.30)with1.30being the current stable release. This means that the published artifact will work with a compiler between version1.30(inclusive) and2(exclusive). So far no difference! Now, times passes and a new stable compiler (1.31) was published. The crate artifact which was already published still works with a compiler between1.30(inclusive) and2(exclusive). But if the crate author opts to publish a new crate version, the MSRV for the new artifact is automatically raised tostable(1.31)which means that it guarantees to work with a compiler version between1.31(inclusive) and2(exclusive). This MSRV change wound not be considered a breaking change since it was already declared before.Therefore a crate author has to options:
1.30.stablewhich means that a future crate version might require a future compiler version (which is at least stable).Anyway, changing the MSRV is considered to be a breaking change which requires a major version bump. (Someone might relax this requirement as I did in the example above and allow a change from e.g.
stableto1.32or1.31to1.30, since that is no compatibility problem).I think, one could take this idea even further and allow labels like
stable - 0.4which means that, a future crate works with the current version minus 4 minor versions. I think this is even a reasonable alternative to LTS releases as proposed in #2483. (Or we could then say thatltsisstableminus one year or another reasonable time considered to be a long term...)Uh oh!
There was an error while loading. Please reload this page.
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.
By the way, this idea does not work for
nightlybecause the nightly compiler does not guarantee backward compatibility.Uh oh!
There was an error while loading. Please reload this page.
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.
Probably my wording was not clear enough, but I've meant almost exactly the same behaviour as was described by you. Automatic conversion from
stableto1.30happens if user executescargo publishwith 1.30 toolchain, in his localCargo.tomlrustfield will be left unchanged, equal tostable. After that user makes some changes to the crate and updates toolchain to stable 1.33. Now oncargo publishfor the next crate versionstablewill be converted to1.33, which will mean>=1.33.0, <2.0.0.For nightlies I currently propose to use
nightly: *as a default option for crates which require nightly compiler, which will mean "any Nightly version".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.
As for LTS releases, they are not only about MSRV, they will also serve as a synchronization point for crate authors and package authors (think Debian). LTS also means that crucial updates (e.g. security or soundness fixes) will be backported to those versions. So I don't think that
stable - 0.4will be able to cover all LTS use-cases.Uh oh!
There was an error while loading. Please reload this page.
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.
To be honest, I am still not sure that we are talking about the same thing: I think, the crucial difference is that in the first case (
rust = 1.30) the published MSRV on crates.io is1.30, while in the second case (rust = stable), the published MSRV isstable(1.30). To be even clearer:1.30is not at all equal tostable(1.30). While they behave equal when it comes to the crate's current version, they are different for the next (patch or minor) version: In the first case the MSRV will be1.30, while in the second case it might be1.30orstable(1.31)or even1.31. Therefore, the crate user can determine from the difference between1.30andstable(1.30)how the crate author answers the question "Is an MSRV change a breaking change?". In the first case, the answer is yes, in the second case it is no!I would like to see a whole passage dedicated to that matter which leaves no room for interpretation... ;-)
I would also like to see a passage dedicated to "version arithmetic" which covers e.g.:
1.30can be1.29,1.28.1, ... in the next crate version.stable(1.30)can be1.29,1.28.1, ... orstable(1.30.1),stable(1.31), ... or1.30,1.30.1,1.31, ... in the next crate version (as long as the rustc is already stabilized).You are right. But I highly doubt the utility of LTS releases for other cases than language version requirements:
stable(1.30) - 0.2.)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.
Okay, I've re-read your RFC and I think now that You've mentioned some of my points. But perhaps, they are still useful to clarify things... ;-)
Btw Thanks for Your effort!
Uh oh!
There was an error while loading. Please reload this page.
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.
Hm, I don't quite get
stable(x)functionality then. The only difference which I can see is thatstable(1.30)will hint thatrustfield was automatically inserted bycargo, otherwise behavior looks similar to me. I'll try to describe current proposal a bit better in a separate PR to my repo. I'll link it later and I will be happy to hear your comments on which parts you think will need additional clarification.As I see it, if public API does not change, then MSRV change will never (well, except the initial migration to using
rustfield) be a breaking change, be it upgrade or downgrade. Dependency versions resolution algorithm will handle a selection of appropriate versions for current toolchain automatically.Don't forget that dependency can be defined as
0.x, so you have not one version, but a set. Thus formula will beMSRV(A) >= max(min( [MSRV(A1), ..MSRV(An)] ), min( [MSRV(B1), ..MSRV(Bn)] )). For stable versions order should be quite simple and you even don't have to calculate it explicitly. Runningcargo publishwith your MSRV toolchain will automatically check if non-empty solution for dependency versions exists.But with "nightly versions" extension things become more complex. You'll have to check that your MSRV condition contains only such nightly versions which can be covered by all of your dependencies.
I am not sure this feature will pulls its weight. Plus it can be added later in a backwards compatible way by a separate proposal.
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.
I was thinking about the theoretical foundations of my proposal and I came up with this article. It is some kind of pre-work for my proposal in this thread but I think its worth sharing here. (Anywhere, I'll try to write another article surrounding these ideas soon.)
An internals thread for a discussion about the article can be found here.