Skip to content
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

docs(ref): Expand on MSRV #14636

Merged
merged 5 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/doc/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* [Overriding Dependencies](reference/overriding-dependencies.md)
* [The Manifest Format](reference/manifest.md)
* [Cargo Targets](reference/cargo-targets.md)
* [Rust version](reference/rust-version.md)
* [Workspaces](reference/workspaces.md)
* [Features](reference/features.md)
* [Features Examples](reference/features-examples.md)
Expand Down
31 changes: 5 additions & 26 deletions src/doc/src/reference/manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Every manifest file consists of the following sections:
* [`version`](#the-version-field) --- The version of the package.
* [`authors`](#the-authors-field) --- The authors of the package.
* [`edition`](#the-edition-field) --- The Rust edition.
* [`rust-version`](#the-rust-version-field) --- The minimal supported Rust version.
* [`rust-version`](rust-version.md) --- The minimal supported Rust version.
* [`description`](#the-description-field) --- A description of the package.
* [`documentation`](#the-documentation-field) --- URL of the package documentation.
* [`readme`](#the-readme-field) --- Path to the package's README file.
Expand Down Expand Up @@ -163,31 +163,9 @@ will have `edition` explicitly specified to a newer value.

### The `rust-version` field

The `rust-version` field is an optional key that tells cargo what version of the
Rust language and compiler your package can be compiled with.
If the currently selected version of the Rust compiler is older than the stated
version, cargo will exit with an error, telling the user what version is
required.
This affects all targets/crates in the package, including test suites,
benchmarks, binaries, examples, etc.

The `rust-version` may be ignored using the `--ignore-rust-version` option.

```toml
[package]
# ...
rust-version = "1.56"
```

The Rust version must be a bare version number with at least one component; it
cannot include semver operators or pre-release identifiers. Compiler pre-release
identifiers such as -nightly will be ignored while checking the Rust version.

To find the minimum `rust-version` compatible with your project, you can use third-party tools like [`cargo-msrv`](https://crates.io/crates/cargo-msrv).

When used on packages that get published, we recommend [verifying the `rust-version`](../guide/continuous-integration.md#verifying-rust-version).

> **MSRV:** Respected as of 1.56
The `rust-version` field tells cargo what version of the
Rust toolchain you support for your package.
See [the Rust version chapter](rust-version.md) for more detail.

### The `description` field

Expand Down Expand Up @@ -685,6 +663,7 @@ more detail.
"#the-exclude-and-include-fields-optional": "manifest.html#the-exclude-and-include-fields",
"#the-publish--field-optional": "manifest.html#the-publish-field",
"#the-metadata-table-optional": "manifest.html#the-metadata-table",
"#rust-version": "rust-version.html",
};
var target = fragments[window.location.hash];
if (target) {
Expand Down
158 changes: 158 additions & 0 deletions src/doc/src/reference/rust-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Rust Version

The `rust-version` field is an optional key that tells cargo what version of the
weihanglo marked this conversation as resolved.
Show resolved Hide resolved
Rust toolchain you support for your package.

```toml
[package]
# ...
rust-version = "1.56"
```

The Rust version must be a bare version number with at least one component; it
cannot include semver operators or pre-release identifiers. Compiler pre-release
identifiers such as -nightly will be ignored while checking the Rust version.

> **MSRV:** Respected as of 1.56

## Uses

**Diagnostics:**

When your package is compiled on an unsupported toolchain,
Cargo will provide clearer diagnostics about the insufficient toolchain version rather than reporting invalid syntax or missing functionality in the standard library.
This affects all [Cargo targets](cargo-targets.md) in the package, including binaries, examples, test suites,
benchmarks, etc.

**Development aid:**

`cargo add` will auto-select the dependency's version requirement to be the latest version compatible with your `rust-version`.
If that isn't the latest version, `cargo add` will inform users so they can make the choice on whether to keep it or update your `rust-version`.

Other tools may also take advantage of it, like `cargo clippy`'s
[`incompatible_msrv` lint](https://rust-lang.github.io/rust-clippy/stable/index.html#/incompatible_msrv).

> **Note:** The `rust-version` may be ignored using the `--ignore-rust-version` option.

## Support Expectations

These are general expectations; some packages may document when they do not follow these.

**Complete:**

All functionality, including binaries and API, are available on the supported Rust versions under every [feature](features.md).

**Verified:**

A package's functionality is verified on its supported Rust versions, including automated testing.
See also our
[Rust version CI guide](../guide/continuous-integration.md#verifying-rust-version).

**Patchable:**

When licenses allow it,
users can [override their local dependency](overriding-dependencies.md) with a fork of your package.
In this situation, Cargo may load the entire workspace for the patched dependency which should work on the supported Rust versions, even if other packages in the workspace have different supported Rust versions.

**Dependency Support:**

In support of the above,
it is expected that each dependency's version-requirement supports at least one version compatible with your `rust-version`.
However,
it is **not** expected that the dependency specification excludes versions incompatible with your `rust-version`.
In fact, supporting both allows you to balance the needs of users that support older Rust versions with those that don't.

## Setting and Updating Rust Version

What Rust versions to support is a trade off between
- Costs for the maintainer in not using newer features of the Rust toolchain or their dependencies
- Costs to users who would benefit from a package using newer features of a toolchain, e.g. reducing build times by migrating to a feature in the standard library from a polyfill
- Availability of a package to users supporting older Rust versions

> **Note:** [Changing `rust-version`](semver.md#env-new-rust) is assumed to be a minor incompatibility

> **Recommendation:** Choose a policy for what Rust versions to support and when that is changed so users can compare it with their own policy and,
> if it isn't compatible,
> decide whether the loss of general improvements or the risk of a blocking bug that won't be fixed is acceptable or not.
>
> The simplest policy to support is to always use the latest Rust version.
>
> Depending on your risk profile, the next simplest approach is to continue to support old major or minor versions of your package that support older Rust versions.

### Selecting supported Rust versions

Users of your package are most likely to track their supported Rust versions to:
- Their Rust toolchain vendor's support policy, e.g. The Rust Project or a Linux distribution
- Note: the Rust Project only offers bug fixes and security updates for the latest version.
- A fixed schedule for users to re-verify their packages with the new toolchain, e.g. the first release of the year, every

In addition, users are unlikely to be using the new Rust version immediately but need time to notice and re-verify or might not be aligned on the exact same schedule..

Example version policies:
- "N-2", meaning "latest version with a 2 release grace window for updating"
- Every even release with a 2 release grace window for updating
- Every version from this calendar year with a one year grace window for updating

> **Note:** To find the minimum `rust-version` compatible with your project as-is, you can use third-party tools like [`cargo-msrv`](https://crates.io/crates/cargo-msrv).

### Update timeline

When your policy specifies you no longer need to support a Rust version, you can update `rust-version` immediately or when needed.

By allowing `rust-version` to drift from your policy,
you offer users more of a grace window for upgrading.
However, this is too unpredictable to be relied on for aligning with the Rust version users track.

The further `rust-version` drifts from your specified policy,
the more likely users are to infer a policy you did not intend,
leading to frustration at the unmet expectations.

When drift is allowed,
there is the question of what is "justifiable enough" to drop supported Versions.
Each person can come to a reasonably different justification;
working through that discussion can be frustrating for the involved parties.
This will disempower those who would want to avoid that type of conflict,
which is particularly the case for new or casual contributors who either
feel that they are not in a position to raise the question or
that the conflict may hurt the chance of their change being merged.

### Multiple Policies in a Workspace

Cargo allows supporting multiple policies within one workspace.

Verifying specific packages under specific Rust versions can get complicated.
Tools like [`cargo-hack`](https://crates.io/crates/cargo-hack) can help.

For any dependency shared across policies,
the lowest common versions must be used as Cargo
[unifies SemVer-compatible versions](resolver.md#semver-compatibility),
potentially limiting access to features of the shared dependency for the workspace member with the higher `rust-version`.

To allow users to patch a dependency on one of your workspace members,
every package in the workspace would need to be loadable in the oldest Rust version supported by the workspace.

### One or More Policies

One way to mitigate the downsides of supporting older Rust versions is to apply your policy to older major or minor versions of your package that you continue to support.
You likely still need a policy for what Rust versions the development branch support compared to the release branches for those major or minor versions.

Only updating the development branch when "needed"' can help reduce the number of supported release branches.

There is the question of what can be backported into these release branches.
By backporting new functionality between minor versions,
the next available version would be missing it which could be considered a breaking change, violating SemVer.
Backporting changes also comes with the risk of introducing bugs.

Supporting older versions comes at a cost.
This cost is dependent on the risk and impact of bugs within the package and what is acceptable for backporting.
Creating the release branches on-demand and putting the backport burden on the community are ways to balance this cost.

There is not yet a way for dependency management tools to report that a non-latest version is still supported,
shifting the responsibility to users to notice this in documentation.

For example, a Rust version support policy could look like:
- The development branch tracks to the latest stable release from the Rust Project, updated when needed
- The minor version will be raised when changing `rust-version`
- The project supports every version for this calendar year, with another year grace window
- The last minor version that supports a supported Rust version will receive community provided bug fixes
- Fixes must be backported to all supported minor releases between the development branch and the needed supported Rust version