Skip to content

[RFC] Minimum Supported Rust Version Revisit #449

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

Closed
wants to merge 5 commits into from
Closed
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
105 changes: 105 additions & 0 deletions rfcs/0000-msrv-2020.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
- Feature Name: msrv-2020
- Start Date: 2020-04-28
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

This RFC proposes an update to the [current MSRV policy].

[current MSRV policy]: https://github.com/rust-embedded/wg/blob/8eb6488fdb16e92e70b074acc2fcf249b3edc70b/ops/msrv.md

# Motivation
[motivation]: #motivation

<!-- Why are we doing this? What use cases does it support? What is the expected outcome? -->

As part of the push to take [foundational crates to 1.0] releases, it has become necessary to be more exact on our guarantees to support versions of the Rust compiler. This discussion [has been contentious] in the past, sparking significant discussions in many meetings.

In particular, it has been necessary to balance the (somewhat opposed) concerns of:

1. Some users may find themselves stuck on old versions of the compiler, due to company restrictions, slow moving distribution/package managers, or regulatory concerns.
2. We maintain these crates on a volunteer basis, and our time is limited to focus on maintenance

The following proposed policy aims to be a "good in most cases" solution, to support the largest subset of users and their typical usage practices.

[foundational crates to 1.0]: https://github.com/rust-embedded/wg/issues/383
[has been contentious]: https://github.com/rust-embedded/wg/issues/427

# Detailed design
[design]: #detailed-design

<!--
This is the bulk of the RFC. Explain the design in enough detail for somebody familiar
with the language to understand, and for somebody familiar with the compiler to implement.
This should get into specifics and corner-cases, and include examples of how the feature is used.
-->

First, let's start with the "what" of the policy, what are our rules?

> NOTE: These rules apply only to released versions of the WG crates, not necessarily the tip of the `main` branch.

* **Required Versions**: Crates maintained by the Embedded WG must always compile on the three most recent stable semver-minor compiler versions.
* e.g. if the current version is `1.50.1`, all WG crates must build with `1.48.*`, `1.49.*`, and `1.50.*`.
* **Minimum Supported Rust Version (MSRV)**: All crates maintained by the Embedded WG must state their **MSRV** in their project README. This must be a version less than or equal to the smallest current **Required Version**.
* The MSRV is inclusive of any possible feature of the crate, as well as the MSRV of all dependencies, unless otherwise specified.
* **Versioning Bump**: When incrementing the **MSRV** of a crate, the crate must make a semver-minor version increment.
* ex 1: For a crate that is version `1.2.3`, it must then go to version `1.3.0`.
* ex 2: For a crate that is version `0.4.5`, it must then go to version `0.4.6`.
* **Best Practices**: When possible, care should be taken to avoid a **Versioning Bump**. However, a **Versioning Bump** is always possible if the above conditions are met, particularly when maintainability of the crate or code simplicity would be improved.


Then, let's look at the "how" of the policy:

* The **MSRV** should be stated in the project README.
* The **MSRV** should be tested using CI. Including:
* Per-commit/per-PR testing
* Periodic CI testing of released crates
* If a WG crate begins failing due to a dependency using new features of Rust, the WG crate should issue a semver-trivial release setting a maximum version of that dependency. Additionally, the WG crate may consider making a **Versioning Bump** without setting the maximum version of that dependency.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the WG crate should issue a semver-trivial release setting a maximum version of that dependency

Are there existing projects that do this?

I've argued against this in the last meeting, and I still feel that this is a bad idea for a general policy. To summarize my thoughts:

  • Defaulting to this will make any consumers of the library miss patch releases with potentially critical bug fixes
  • I believe Cargo's default behavior is to only pull in one semver-compatible version of a crate, but this might break when a maximum version constraint is used (since those are very uncommon). Either way, it will lead to either of these two outcomes:
    • The new release of the dependency will not be used at all because of this constraint, even if other dependencies are fine with the new version.
    • Two versions of the dependency will be used by every crate that depends on the WG crate and an unrelated crate that also pulls in the dependency. (This means there's also no way to opt out here; you'll still get the old version even if you use the new version too, which implies that you're fine with a newer Rust version)

As such, I feel like this solution is not fit to be part of the general MSRV policy. It can still make sense to do this, but that's a case-by-case decision. I think the general policy should be to just bump the WG crate's version accordingly with the next release. Also note that we shouldn't feel like we're forced to do the new release immediately, as an MSRV bump is just a documentation change, so it can be queued up with other changes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there existing projects that do this?

I'm not aware of any other projects that do this. I don't have a huge amount of visibility, though.

Defaulting to this will make any consumers of the library miss patch releases with potentially critical bug fixes

That being said, I did try to mitigate this approach by suggesting this in terms of documentation:

  • For users that are sensitive to MSRV changes, we should suggest that they specify the Major and Minor semver version, e.g. version = "1.5.*".
  • For all other users, we should suggest they specify only the Major semver version, e.g. version = "1.*".

This would be the difference between "taking changes, including ones that break MSRV", and "do not take changes that break MSRV".

Without setting an upper bound on the dependency, then we have implicitly increased the MSRV, which at least as described in this RFC, would require a version bump.

but this might break when a maximum version constraint is used

I would suggest that this might be an issue that should be fixed upstream in Cargo itself, if it is something that "should" work.

Two versions of the dependency will be used by every crate that depends on the WG crate and an unrelated crate that also pulls in the dependency. (This means there's also no way to opt out here; you'll still get the old version even if you use the new version too, which implies that you're fine with a newer Rust version)

Couldn't this be fixed by an implicit cargo update -p msrv_breaking_crate step to force re-consolidation of versions? Not sure if I'm completely understanding.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For users that are sensitive to MSRV changes, we should suggest that they specify the Major and Minor semver version, e.g. version = "1.5.*".

Does this work in practice or does Cargo have any pitfalls with * requirements? Also, the default recommended format should probably just be the usual version = "1.x.y", not version = "1.*".

Couldn't this be fixed by an implicit cargo update -p msrv_breaking_crate step to force re-consolidation of versions? Not sure if I'm completely understanding.

If the WG crate specifies an upper bound, cargo update must respect that. If any other crate does not specify that bound and pulls in a second, newer version, cargo update will not do anything since it doesn't downgrade unless you tell it to choose a specific version of the dependency.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but this might break when a maximum version constraint is used

I would suggest that this might be an issue that should be fixed upstream in Cargo itself, if it is something that "should" work.

I'm not sure it's a good idea. Pulling a single version of dependency instead of multiple of them is better: multiple versions of the same crate can lead to multiple incompatible interfaces.

* Pull requests that *lower* the MSRV will be accepted. However, if the **MSRV** is reduced, a new **Versioning Bump** will be required to increase it again. Example:
* A WG crate has an **MSRV** of `1.50.0`, and a version of `1.2.5`.
* A change is made that requires a new version of Rust.
* The WG crate updates its **MSRV** to `1.52.0`, and its version to `1.3.0`.
* A PR is submitted to lower the **MSRV** to `1.50.1`.
* The WG crate updates its **MSRV** to `1.50.1`, and its version to `1.3.1`.
* A change is made that raises the **MSRV** to `1.52.0` again.
* The WG crate updates its **MSRV** to `1.52.0`, and its version to `1.4.0`.
* When necessary or desired, a crate may consider supporting multiple "release trains" simultaneously. For example, it may decide to maintain a version of `1.4.x` that supports **MSRV** `1.50.0`, and a version of `1.5.x` that supports **MSRV** `1.52.0`. This is up to the maintainers of that crate.
* For users that are sensitive to **MSRV** changes, we should suggest that they specify the Major and Minor semver version, e.g. `version = "1.5.*"`.
* For all other users, we should suggest they specify only the Major semver version, e.g. `version = "1.*"`.

# How We Teach This
[how-we-teach-this]: #how-we-teach-this

<!--
What names and terminology work best for these concepts and why?
How is this idea best presented—as a continuation of existing Rust patterns, or as a wholly new one?

Would the acceptance of this proposal change how Rust is taught to new users at any level?
How should this feature be introduced and taught to existing Rust users?

What additions or changes to the Rust Reference, _The Rust Programming Language_, and/or _Rust by Example_ does it entail?
-->

We will need to take the following actions:

1. Update the [MSRV Operational Notes](./../ops/msrv.md)
2. Ensure all crates test their current MSRV

# Drawbacks
[drawbacks]: #drawbacks

The MSRV process is hugely time consuming to maintain, discuss, and decide. Likely, it currently provides little-to-no-value for most users.

Although the process detailed above is comprehensive as far as we know today, it is likely to have unexpected edge cases that will require refinement.

# Alternatives
[alternatives]: #alternatives

The primary alternative to this is to abolish the management of MSRV for Embedded Rust WG projects. This would simplify the effort of managing these releases and projects, however for esoteric users that are restricted to an old version of the compiler, this would require extra effort on their part to maintain or use the crates in question.

# Unresolved questions
[unresolved]: #unresolved-questions

At the moment, there are no open unresolved questions.