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

Clean up the editions/index.md page #294

Merged
merged 1 commit into from
Mar 26, 2024
Merged
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
96 changes: 35 additions & 61 deletions src/editions/index.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,37 @@
# What are Editions?

The release of Rust 1.0
([in May 2015](https://blog.rust-lang.org/2015/05/15/Rust-1.0.html))
established
["stability without stagnation"](https://blog.rust-lang.org/2014/10/30/Stability.html)
as a core Rust deliverable.
Ever since the 1.0 release,
the rule for Rust has been that once a feature has been released on stable,
we are committed to supporting that feature for all future releases.

There are times, however, when it is useful to be able to make small changes
to the language that are not backwards compatible.
The most obvious example is introducing a new keyword,
which would invalidate variables with the same name.
For example, the first version of Rust did not have the `async` and `await` keywords.
Suddenly changing those words to keywords in a later version would've broken code like `let async = 1;`.

**Editions** are the mechanism we use to solve this problem.
When we want to release a feature that would otherwise be backwards incompatible,
we do so as part of a new Rust *edition*.
Editions are opt-in, and so existing crates do
not see these changes until they explicitly migrate over to the new edition.
This means that even the latest version of Rust will still *not* treat `async` as a keyword,
unless edition 2018 or later is chosen.
This choice is made *per crate* [as part of its `Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-edition-field).
New crates created by `cargo new` are always configured to use the latest stable edition.

### Editions do not split the ecosystem

The most important rule for editions is that crates in one edition can
interoperate seamlessly with crates compiled in other editions. This ensures
that the decision to migrate to a newer edition is a "private one" that the
crate can make without affecting others.

The requirement for crate interoperability implies some limits on the kinds of
changes that we can make in an edition.
In general, changes that occur in an edition tend to be "skin deep".
All Rust code, regardless of edition,
is ultimately compiled to the same internal representation within the compiler.

### Edition migration is easy and largely automated

Our goal is to make it easy for crates to upgrade to a new edition.
When we release a new edition,
we also provide [tooling to automate the migration](https://doc.rust-lang.org/cargo/commands/cargo-fix.html).
It makes minor changes to your code necessary to make it compatible with the new edition.
For example, when migrating to Rust 2018, it changes anything named `async` to use the equivalent
[raw identifier syntax](https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html): `r#async`.

The automated migrations are not necessarily perfect:
there might be some corner cases where manual changes are still required.
The tooling tries hard to avoid changes
to semantics that could affect the correctness or performance of the code.

In addition to tooling, we also maintain this Rust Edition Guide that covers
the changes that are part of an edition.
This guide describes each change and gives pointers to where you can learn more about it.
It also covers any corner cases or details you should be aware of.
This guide serves as an overview of editions,
as a migration guide for specific editions,
and as a quick troubleshooting reference
if you encounter problems with the automated tooling.
In May 2015, the [release of Rust 1.0](https://blog.rust-lang.org/2015/05/15/Rust-1.0.html) established "[stability without stagnation](https://blog.rust-lang.org/2014/10/30/Stability.html)" as a core Rust axiom. Since then, Rust has committed to a pivotal rule: once a feature is [released through stable](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html), contributors will continue to support that feature for all future releases.

However, there are times when it's useful to make backwards-incompatible changes to the language. A common example is the introduction of a new keyword. For instance, early versions of Rust didn't feature the `async` and `await` keywords.

If Rust had suddenly introduced these new keywords, some code would have broken: `let async = 1;` would no longer work.

Rust uses **editions** to solve this problem. When there are backwards-incompatible changes, they are pushed into the next edition. Since editions are opt-in, existing crates won't use the changes unless they explicitly migrate into the new edition. For example, the latest version of Rust doesn't treat `async` as a keyword unless edition 2018 or later is chosen.

Each crate chooses its edition [within its `Cargo.toml` file](https://doc.rust-lang.org/cargo/reference/manifest.html#the-edition-field). When creating a new crate with Cargo, it will automatically select the newest stable edition.

## Editions do not split the ecosystem

When creating editions, there is one most consequential rule: crates in one edition **must** seamlessly interoperate with those compiled with other editions.

In other words, each crate can decide when to migrate to a new edition independently. This decision is 'private' - it won't affect other crates in the ecosystem.

For Rust, this required compatibility implies some limits on the kinds of changes that can be featured in an edition. As a result, changes found in new Rust editions tend to be 'skin deep'. All Rust code - regardless of edition - will ultimately compile down to the same internal representation within the compiler.

## Edition migration is easy and largely automated

Rust aims to make upgrading to a new edition an easy process. When a new edition releases, crate authors may use [automatic migration tooling within `cargo`](https://doc.rust-lang.org/cargo/commands/cargo-fix.html) to migrate. Cargo will then make minor changes to the code to make it compatible with the new version.

For example, when migrating to Rust 2018, anything named `async` will now use the equivalent [raw identifier syntax](https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html): `r#async`.

Cargo's automatic migrations aren't perfect: there may still be corner cases where manual changes are required. It aims to avoid changes to semantics that could affect the correctness or performance of the code.

## What this guide covers

In addition to tooling, this Rust Edition Guide also covers the changes that are part of each edition. It describes each change and links to additional details, if available. It also covers corner cases or tricky details crate authors should be aware of.

Crate authors should find:

- An overview of editions
- A migration guide for specific editions
- A quick troubleshooting reference when automated tooling isn't working.