|
| 1 | +- Feature Name: min_rust_version |
| 2 | +- Start Date: 2018-06-28 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Add `rust` field to the package section of `Cargo.toml` which will be used to |
| 10 | +specify crate's Minimum Supported Rust Version (MSRV): |
| 11 | +```toml |
| 12 | +[package] |
| 13 | +name = "foo" |
| 14 | +version = "0.1.0" |
| 15 | +rust = "1.30" |
| 16 | +``` |
| 17 | + |
| 18 | +# Motivation |
| 19 | +[motivation]: #motivation |
| 20 | + |
| 21 | +Currently crates have no way to formally specify MSRV. As a result users can't |
| 22 | +check if crate can be built on their toolchain without building it. It also |
| 23 | +leads to the debate on how to handle crate version change on bumping MSRV, |
| 24 | +conservative approach is to consider such changes as breaking ones, which can |
| 25 | +hinder adoption of new features across ecosystem or result in version number |
| 26 | +inflation, which makes it harder to keep downstream crates up-to-date. More |
| 27 | +relaxed approach on another hand can result in broken crates for user of older |
| 28 | +compiler versions. |
| 29 | + |
| 30 | +# Guide-level explanation |
| 31 | +[guide-level-explanation]: #guide-level-explanation |
| 32 | + |
| 33 | +`cargo init` will automatically create `Cargo.toml` with `rust` field equal to |
| 34 | +`rust="stable"` or `rust="nightly"` depending on the currently used toolcahin. |
| 35 | +On `cargo publish` cargo will take currently used Rust compiler version and |
| 36 | +will insert it before uploading the crate. In other words localy your `Cargo.toml` |
| 37 | +willl still have `rust="stable"`, but version sent to crates.io will have |
| 38 | +`rust="1.30"` if you've used Rust 1.30. This version will be used to determine if |
| 39 | +crate can be used with the crate user's toolchain and to select appropriate |
| 40 | +dependency versions. In case if you have `rust="stable"`, but execute |
| 41 | +`cargo publish` with Nigthly toolcahin you will get an error. |
| 42 | + |
| 43 | +If you are sure that your crate supports older Rust versions (e.g. by using CI |
| 44 | +testing) you can change `rust` field accordingly. On `cargo publish` it will be |
| 45 | +checked that crate indeed can be built with the specified version. (though this |
| 46 | +check can be disabled with `--no-verify` option) |
| 47 | + |
| 48 | +For example, lets imagine that your crate depends on crate `foo` with 10 published |
| 49 | +versions from `0.1.0` to `0.1.10`, in versions from `0.1.0` to `0.1.5` `rust` |
| 50 | +field in the `Cargo.toml` sent to crates.io equals to "1.30" and for others to |
| 51 | +"1.40". Now if you'll build your project with Rust 1.33 `cargo` will select |
| 52 | +`foo v0.1.5`, and `foo v0.1.10` if you'll build your project with Rust 1.30 or |
| 53 | +later. But if you'll try to build your project with Rust 1.29 cargo will issue an |
| 54 | +error. Although this check can be disabled with `--no-rust-check` option. |
| 55 | + |
| 56 | +`rust` field should respect the following minimal requirements: |
| 57 | +- value should be equal to "stable", "nigthly" or to a version in semver format |
| 58 | +("1.50" is a valid value and implies "1.50.0") |
| 59 | +- version should not be bigger than the current stable toolchain |
| 60 | +- version should not be smaller than 1.27 (version in which `package.rust` field |
| 61 | +became a warning instead of an error) |
| 62 | + |
| 63 | +`rust` will be a required field. For crates uploaded before introduction of this |
| 64 | +feature 2015 edition crates will imply `rust="1.0"` and 2018 ediiton will imply |
| 65 | +`rust = "1.30"`. |
| 66 | + |
| 67 | +It will be an error to use `rust="1.27"` and `edition="2018"`, but `rust="1.40"` and `edition="2015"` is a valid combination. |
| 68 | + |
| 69 | +# Reference-level explanation |
| 70 | +[reference-level-explanation]: #reference-level-explanation |
| 71 | + |
| 72 | +The describe functionality can be introduced in several stages: |
| 73 | + |
| 74 | + |
| 75 | +## First stage: dumb field |
| 76 | + |
| 77 | +At first the `rust` field can be simply a declarative optional field without any |
| 78 | +functionality behind it. The reason for it is to reduce implementation cost of |
| 79 | +the first stage to the minimum and ideally ship it as part of Rust 2018. |
| 80 | +It will also allow crate authors who care about MSRV to start mark their crates |
| 81 | +early. |
| 82 | + |
| 83 | +## Second stage: versions resolution |
| 84 | + |
| 85 | +`rust` field becomes required and cargo will add it as a constraint to dependency |
| 86 | +versions resolution. If user uses e.g. Rust 1.40 and uses crate `foo = "0.2"`, but |
| 87 | +all selected versions of `foo` specify MSRV e.g. equal 1.41 or bigger (or even |
| 88 | +nightly) `cargo` will issue an error. |
| 89 | + |
| 90 | +`rust` field value will be checked as well, on crate build `cargo` will check if |
| 91 | +all upstream dependencies can be built with the specified MSRV. (i.e. it will |
| 92 | +check if there is exists solution for given crates and Rust versions constraints) |
| 93 | + |
| 94 | +Yanked crates will be ignored in this process. |
| 95 | + |
| 96 | +Implementing this functionality hopefully will allow to close the debate regarding |
| 97 | +MSRV handling in crate versions and will allow crate authors to feel less |
| 98 | +restrictive about bumping their crate's MSRV. (though it can be a usefull |
| 99 | +convention for post-1.0 crates to bump minor version on MSRV change to allow |
| 100 | +publishing backports which fix serious issues using patch version) |
| 101 | + |
| 102 | +## Third stage: better crate checks |
| 103 | + |
| 104 | +Here we introduce two-level check for crates. First level will check if all used |
| 105 | +items were stabilised before or on given MSRV using `#[stable(since=version)]` |
| 106 | +attribute, issuing compile errors otherwise. |
| 107 | + |
| 108 | +Second level will try to build crate with the specified MSRV on `cargo publish`, |
| 109 | +i.e. words it will be required to install MSRV toolchain. (this check can be |
| 110 | +disabled using `--no-verify` option) |
| 111 | + |
| 112 | +While these two checks will not replace proper CI testing, they will help to |
| 113 | +reduce number of improper MSRV configuration to the minimum. |
| 114 | + |
| 115 | +Note that `rust` field must be equal to MSRV with default features for all |
| 116 | +supported targets. |
| 117 | + |
| 118 | +## Extension: nightly versions |
| 119 | + |
| 120 | +For some bleeding-edge crates which experience frequent breaks on Nightly updates |
| 121 | +(e.g. `rocket`) it can be useful to specify exact Nigthly version(s) on which |
| 122 | +crate can be built. One way to achieve this is by using the following syntax: |
| 123 | +- single version: rust = "nightly: 2018-01-01" |
| 124 | +- enumeration: "nightly: 2018-01-01, 2018-01-15" |
| 125 | +- (inclusive) range: "nightly: 2018-01-01..2018-01-15" |
| 126 | +- enumeration+range: "nightly: 2018-01-01, 2018-01-08..2018-01-15" |
| 127 | + |
| 128 | +Such restrictions can be quite severe, but hopefully this functionality will be |
| 129 | +used only by handful of crates. |
| 130 | + |
| 131 | +## Extension: cfg based MSRV |
| 132 | + |
| 133 | +Some crates can have different MSRVs depending on target architecture or enabled |
| 134 | +features. In such cases it can be usefull to extend `rust` field, e.g. in the |
| 135 | +following way: |
| 136 | +```toml |
| 137 | +rust = "1.30" |
| 138 | +rust-cases = [ |
| 139 | + { cfg = "x86_64-pc-windows-gnu", version = "1.35" }, |
| 140 | + { cfg = 'cfg(feature = "foo")', version = "1.33" }, |
| 141 | +] |
| 142 | +``` |
| 143 | + |
| 144 | +Version resolution will filter all cases with `cfg` equal to true and will take |
| 145 | +max `version` value from them as a MSRV. If all `cfg`s are false, value in the |
| 146 | +`rust` field will be used. |
| 147 | + |
| 148 | +# Drawbacks |
| 149 | +[drawbacks]: #drawbacks |
| 150 | + |
| 151 | +- Declaration of MSRV and describe checks does not guarantee that, only |
| 152 | +appropriate CI testing. |
| 153 | +- More complex dependency versions resolution algorithm. |
| 154 | +- MSRV selected by `cargo publish` can be overly conservative. |
| 155 | +- MSRV checks will make compiler more complex. |
| 156 | + |
| 157 | +# Rationale and Alternatives |
| 158 | +[alternatives]: #alternatives |
| 159 | + |
| 160 | +- Automatically calculate MSRV. |
| 161 | +- Do nothing and rely on [LTS releases](https://github.com/rust-lang/rfcs/pull/2483) |
| 162 | +for bumping crate MSRVs. |
| 163 | + |
| 164 | +# Prior art |
| 165 | +[prior-art]: #prior-art |
| 166 | + |
| 167 | +Previous proposals: |
| 168 | +- [RFC 1707](https://github.com/rust-lang/rfcs/pull/1707) |
| 169 | +- [RFC 1709](https://github.com/rust-lang/rfcs/pull/1709) |
| 170 | +- [RFC 1953](https://github.com/rust-lang/rfcs/pull/1953) |
| 171 | +- [RFC 2182](https://github.com/rust-lang/rfcs/pull/2128) (arguably this one got off-track) |
| 172 | + |
| 173 | +# Unresolved questions |
| 174 | +[unresolved]: #unresolved-questions |
| 175 | + |
| 176 | +- Name bike-shedding: `rust` vs `rustc` vs `min-rust-version` |
| 177 | +- Additional checks? |
| 178 | +- Better description of versions resolution algorithm. |
0 commit comments