Skip to content

Commit 2d07263

Browse files
m-ou-senikomatsakis
andcommitted
Edition 2021 post.
Co-authored-by: Niko Matsakis <[email protected]>
1 parent 077c4c0 commit 2d07263

File tree

1 file changed

+350
-0
lines changed

1 file changed

+350
-0
lines changed

posts/2021-04-30-edition-2021.md

+350
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
---
2+
layout: post
3+
title: "The Plan for the Rust 2021 Edition"
4+
author: Mara Bos
5+
team: The Rust 2021 Edition Working Group <https://www.rust-lang.org/governance/teams/core#project-edition-2021>
6+
---
7+
8+
We are happy to announce that the third edition of the Rust language, Rust 2021,
9+
is scheduled for release in October.
10+
Rust 2021 contains a number of small changes that are
11+
nonetheless expected to make a significant improvement to how Rust feels in practice.
12+
13+
## What is an Edition?
14+
15+
The release of Rust 1.0 established
16+
["stability without stagnation"](https://blog.rust-lang.org/2014/10/30/Stability.html)
17+
as a core Rust deliverable.
18+
Ever since the 1.0 release,
19+
the rule for Rust has been that once a feature has been released on stable,
20+
we are committed to supporting that feature for all future releases.
21+
22+
There are times, however, when it is useful to be able to make small changes
23+
to the language that are not backwards compatible.
24+
The most obvious example is introducing a new keyword,
25+
which would invalidate variables with the same name.
26+
For example, the first version of Rust did not have the `async` and `await` keywords.
27+
Suddenly changing those words to keywords in a later version would've broken code like `let async = 1;`.
28+
29+
**Editions** are the mechanism we use to solve this problem.
30+
When we want to release a feature that would otherwise be backwards incompatible,
31+
we do so as part of a new Rust *edition*.
32+
Editions are opt-in, and so existing crates do
33+
not see these changes until they explicitly migrate over to the new edition.
34+
This means that even the latest version of Rust will still *not* treat `async` as a keyword,
35+
unless edition 2018 or later is chosen.
36+
This choice is made *per crate* [as part of its `Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-edition-field).
37+
New crates created by `cargo new` are always configured to use the latest stable edition.
38+
39+
### Editions do not split the ecosystem
40+
41+
The most important rule for editions is that crates in one edition can
42+
interoperate seamlessly with crates compiled in other editions. This ensures
43+
that the decision to migrate to a newer edition is a "private one" that the
44+
crate can make without affecting others.
45+
46+
The requirement for crate interoperability implies some limits on the kinds of
47+
changes that we can make in an edition.
48+
In general, changes that occur in an edition tend to be "skin deep".
49+
All Rust code, regardless of edition,
50+
is ultimately compiled to the same internal representation within the compiler.
51+
52+
### Edition Migration is easy and largely automated
53+
54+
Our goal is to make it easy for crates to upgrade to a new edition.
55+
When we release a new edition,
56+
we also provide [tooling to automate the migration](https://doc.rust-lang.org/cargo/commands/cargo-fix.html).
57+
It makes minor changes to your code necessary to make it compatible with the new edition.
58+
For example, when migrating to Rust 2018, it changes anything named `async` to use the equivalent
59+
[raw identifier syntax](https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html): `r#async`.
60+
61+
The automated migrations are not necessarily perfect:
62+
there might be some corner cases where manual changes are still required.
63+
The tooling tries hard to avoid changes
64+
to semantics that could affect the correctness or performance of the code.
65+
66+
In addition to tooling, we also maintain an Edition Migration Guide that covers
67+
the changes that are part of an edition.
68+
This guide will describe the change and give pointers to where people can learn more about it.
69+
It will also cover any corner cases or details that people should be aware of.
70+
The guide serves both as an overview of the edition,
71+
but also as a quick troubleshooting reference
72+
if people encounter problems with the automated tooling.
73+
74+
## What changes are planned for Rust 2021?
75+
76+
Over the last few months, the Rust 2021 Working Group has
77+
gone through a number of proposals for what to include in the new edition.
78+
We are happy to announce the final list of edition changes.
79+
Each feature had to meet two criteria to make this list.
80+
First, they had to be approved by the appropriate Rust team(s).
81+
Second, their implementation had to be far enough along that we had
82+
confidence that they would be completed in time for the planned milestones.
83+
84+
### Additions to the prelude
85+
86+
The [prelude of the standard library](https://doc.rust-lang.org/stable/std/prelude/index.html)
87+
is the module containing everything that is automatically imported in every module.
88+
It contains commonly used items such as `Option`, `Vec`, `drop`, and `Clone`.
89+
90+
The Rust compiler prioritizes any manually imported items over those
91+
from the prelude, to make sure additions to the prelude will not break any existing code.
92+
For example, if you have a crate or module called `example` containing a `pub struct Option;`,
93+
then `use example::*;` will make `Option` unambiguously refer to the one from `example`;
94+
not the one from the standard library.
95+
96+
However, adding a *trait* to the prelude can break existing code in a subtle way.
97+
A call to `x.try_into()` using a `MyTryInto` trait might become ambiguous and
98+
fail to compile if `std`'s `TryInto` is also imported,
99+
since it provides a method with the same name.
100+
This is the reason we haven't added `TryInto` to the prelude yet,
101+
since there is a lot of code that would break this way.
102+
103+
As a solution, Rust 2021 will use a new prelude.
104+
It's identical to the current one, except for three new additions:
105+
106+
- [`std::convert::TryInto`](https://doc.rust-lang.org/stable/std/convert/trait.TryInto.html)
107+
- [`std::convert::TryFrom`](https://doc.rust-lang.org/stable/std/convert/trait.TryFrom.html)
108+
- [`std::iter::FromIterator`](https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html)
109+
110+
The library team still needs to formally approve these, which will likely happen soon.
111+
<!-- TODO: hopefully this happens before we publish this -->
112+
113+
### Default Cargo feature resolver
114+
115+
Since Rust 1.51.0, Cargo has opt-in support for a [new feature resolver][4]
116+
which can be activated with `resolver = "2"` in `Cargo.toml`.
117+
118+
Starting in Rust 2021, this will be the default.
119+
That is, writing `edition = "2021"` in `Cargo.toml` will imply `resolver = "2"`.
120+
121+
The new feature resolver no longer merges all requested features for
122+
crates that are depended on in multiple ways.
123+
See [the announcement of Rust 1.51][5] for details.
124+
125+
[4]: https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2
126+
[5]: https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#cargos-new-feature-resolver
127+
128+
### IntoIterator for arrays
129+
130+
Until Rust 1.53, only *references* to arrays implement `IntoIterator`.
131+
This means you can iterate over `&[1, 2, 3]` and `&mut [1, 2, 3]`,
132+
but not over `[1, 2, 3]` directly.
133+
134+
```rust
135+
for &e in &[1, 2, 3] {} // Ok :)
136+
137+
for e in [1, 2, 3] {} // Error :(
138+
```
139+
140+
This has been [a long-standing issue][25], but the solution is not as simple as it seems.
141+
Just [adding the trait implementation][20] would break existing code.
142+
`array.into_iter()` already compiles today because that implicitly calls
143+
`(&array).into_iter()` due to [how method call syntax works][22].
144+
Adding the trait implementation would change the meaning.
145+
146+
Usually we categorize this type of breakage
147+
(adding a trait implementation) 'minor' and acceptable.
148+
But in this case there is too much code that would be broken by it.
149+
150+
It has been suggested many times to "only implement `IntoIterator` for arrays in Rust 2021".
151+
However, this is simply not possible.
152+
You can't have a trait implementation exist in one edition and not in another,
153+
since editions can be mixed.
154+
155+
Instead, we decided to add the trait implementation in all editions (starting in Rust 1.53.0),
156+
but add a small hack to avoid breakage until Rust 2021.
157+
In Rust 2015 and 2018 code, the compiler will still resolve `array.into_iter()`
158+
to `(&array).into_iter()` like before, as if the trait implementation does not exist.
159+
This *only* applies to the `.into_iter()` method call syntax.
160+
It does not affect any other syntax such as `for e in [1, 2, 3]` or `iter.zip([1, 2, 3])`.
161+
Those will start to work in *all* editions.
162+
163+
While it's a shame that this required a small hack to avoid breakage,
164+
we're very happy with how this solution keeps the difference between
165+
the editions to an absolute minimum.
166+
167+
[25]: https://github.com/rust-lang/rust/issues/25725
168+
[20]: https://github.com/rust-lang/rust/pull/65819
169+
[22]: https://doc.rust-lang.org/book/ch05-03-method-syntax.html#wheres-the---operator
170+
171+
### Disjoint capture in closures
172+
173+
[Closures](https://doc.rust-lang.org/book/ch13-01-closures.html)
174+
automatically capture anything that you refer to from within their body.
175+
For example, `|| a + 1` automatically captures a reference to `a` from the surrounding context.
176+
177+
Currently, this applies to whole structs, even when only using one field.
178+
For example, `|| a.x + 1` captures a reference to `a` and not just `a.x`.
179+
In some situations, this is a problem.
180+
When a field of the struct is already borrowed (mutably) or moved out of,
181+
the other fields can no longer be used in a closure,
182+
since that would capture the whole struct, which is no longer available.
183+
184+
```rust
185+
let a = SomeStruct::new();
186+
187+
drop(a.x); // Move out of one field of the struct
188+
189+
println!("{}", a.y); // Ok: Still use another field of the struct
190+
191+
let c = || println!("{}", a.y); // Error: Tries to capture all of `a`
192+
c();
193+
```
194+
195+
Starting in Rust 2021, closures will only capture the fields that they use.
196+
So, the above example will compile fine in Rust 2021.
197+
198+
This new behavior is only activated in the new edition,
199+
since it can change the order in which fields are dropped.
200+
As for all edition changes, an automatic migration is available,
201+
which will update your closures for which this matters.
202+
It can insert `let _ = &a;` inside the closure to force the entire
203+
struct to be captured as before.
204+
205+
### Panic macro consistency
206+
207+
The `panic!()` macro is one of Rust's most well known macros.
208+
However, it has [some subtle surprises](https://github.com/rust-lang/rfcs/blob/master/text/3007-panic-plan.md)
209+
that we can't just change due to backwards compatibility.
210+
211+
```rust
212+
panic!("{}", 1); // Ok, panics with the message "1"
213+
panic!("{}"); // Ok, panics with the message "{}"
214+
```
215+
216+
The `panic!()` macro only uses string formatting when it's invoked with more than one argument.
217+
When invoked with a single argument, it doesn't even look at that argument.
218+
219+
```rust
220+
let a = "{";
221+
println!(a); // Error: First argument must be a format string literal
222+
panic!(a); // Ok: The panic macro doesn't care
223+
```
224+
225+
(It even accepts non-strings such as `panic!(123)`, which is uncommon and rarely useful.)
226+
227+
This will especially be a problem once
228+
[implicit format arguments](https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html)
229+
are stabilized.
230+
That feature will make `println!("hello {name}")` a short-hand for `println!("hello {}", name)`.
231+
However, `panic!("hello {name}")` would not work as expected,
232+
since `panic!()` doesn't process a single argument as format string.
233+
234+
To avoid that confusing situation, Rust 2021 features a more consistent `panic!()` macro.
235+
The new `panic!()` macro will no longer accept arbitrary expressions as the only argument.
236+
It will, just like `println!()`, always process the first argument as format string.
237+
Since `panic!()` will no longer accept arbitrary payloads,
238+
[`panic_any()`](https://doc.rust-lang.org/stable/std/panic/fn.panic_any.html)
239+
will be the only way to panic with something other than a formatted string.
240+
241+
In addition, `core::panic!()` and `std::panic!()` will be identical in Rust 2021.
242+
Currently, there are some historical differences between those two,
243+
which can be noticable when switching `#![no_std]` on or off.
244+
245+
### Reserving syntax
246+
247+
To make space for some new syntax in the future,
248+
we've decided to reserve syntax for prefixed identifiers and literals:
249+
`prefix#identifier`, `prefix"string"`, `prefix'c'`, and `prefix#123`,
250+
where `prefix` can be any identifier.
251+
(Except those that already have a meaning, such as `b''` and `r""`.)
252+
253+
This is a breaking change, since macros can currently accept `hello"world"`,
254+
which they will see as two separate tokens: `hello` and `"world"`.
255+
The (automatic) fix is simple though. Just insert a space: `hello "world"`.
256+
257+
<!--
258+
The original plan was to reserve only `k#` and `f""` for future use,
259+
but reserving *all* possible prefixes did not have many downsides.
260+
It leaves more space for new syntax which would otherwise need to wait for another edition.
261+
-->
262+
263+
Other than turning these into a tokenization error,
264+
[the RFC][10] does not attach a meaning to any prefix yet.
265+
Assigning meaning to specific prefixes is left to future proposals,
266+
which will&mdash;thanks to reserving these prefixes now&mdash;not be breaking changes.
267+
268+
These are some new prefixes you might see in the future:
269+
270+
- `f""` as a short-hand for a format string.
271+
For example, `f"hello {name}"` as a short-hand for the equivalent `format_args!()` invocation.
272+
273+
- `c""` or `z""` for null-terminated C strings.
274+
275+
- `k#keyword` to allow writing keywords that don't exist yet in the current edition.
276+
For example, while `async` is not a keyword in edition 2015,
277+
this prefix would've allowed us to accept `k#async` in edition 2015
278+
without having to wait for edition 2018 to reserve `async` as a keyword.
279+
280+
[10]: https://github.com/rust-lang/rfcs/pull/3101
281+
282+
### Promoting two warnings to hard errors
283+
284+
Two existing lints are becoming hard errors in Rust 2021.
285+
These lints will remain warnings in older editions.
286+
287+
* `bare-trait-objects`:
288+
The use of the `dyn` keyword to identify [trait objects](https://doc.rust-lang.org/book/ch17-02-trait-objects.html)
289+
will be mandatory in Rust 2021.
290+
291+
* `ellipsis-inclusive-range-patterns`:
292+
The [deprecated `...` syntax](https://doc.rust-lang.org/stable/reference/patterns.html#range-patterns)
293+
for inclusive range patterns is no longer accepted in Rust 2021.
294+
It has been superseded by `..=`, which is consistent with expressions.
295+
296+
### Or patterns in macro_rules
297+
298+
Starting in Rust 1.53.0, [patterns](https://doc.rust-lang.org/stable/reference/patterns.html)
299+
are extended to support `|` nested anywhere in the pattern.
300+
This enables you to write `Some(1 | 2)` instead of `Some(1) | Some(2)`.
301+
Since this was simply not allowed before, this is not a breaking change.
302+
303+
However, this change also affects [`macro_rules` macros](https://doc.rust-lang.org/stable/reference/macros-by-example.html).
304+
Such macros can accept patterns using the `:pat` fragment specifier.
305+
Currently, `:pat` does *not* match `|`, since before Rust 1.53,
306+
not all patterns (at all nested levels) could contain a `|`.
307+
Macros that accept patterns like `A | B`,
308+
such as [`matches!()`](https://doc.rust-lang.org/1.51.0/std/macro.matches.html)
309+
use something like `$($_:pat)|+`.
310+
Because we don't want to break any existing macros,
311+
we did *not* change the meaning of `:pat` in Rust 1.53.0 to include `|`.
312+
313+
Instead, we will make that change as part of Rust 2021.
314+
In the new edition, the `:pat` fragment specifier *will* match `A | B`.
315+
316+
Since there are times that one still wishes to match a single pattern
317+
variant without `|`, the fragment specified `:pat_param` has been added
318+
to retain the older behavior.
319+
The name refers to its main use case: a pattern in a closure parameter.
320+
321+
## What comes next?
322+
323+
Our plan is to have these changes merged and fully tested by September,
324+
to make sure the 2021 edition makes it into Rust 1.56.0.
325+
Rust 1.56.0 will then be in beta for six weeks,
326+
after which it is released as stable on October 21st.
327+
328+
However, note that Rust is a project run by volunteers.
329+
We prioritize the personal well-being of everyone working on Rust
330+
over any deadlines and expectations we might have set.
331+
This could mean delaying the edition a version if necessary,
332+
or dropping a feature that turns out to be too difficult or stressful to finish in time.
333+
334+
That said, we are on schedule and many of the difficult problems are already tackled,
335+
thanks to all the people contributing to Rust 2021! 💛
336+
337+
---
338+
339+
You can expect another announcement about the new edition in July.
340+
At that point we expect all changes and automatic migrations to be implemented
341+
and ready for public testing.
342+
343+
We'll be posting some more details about the process and rejected proposals on
344+
the "Inside Rust" blog soon.
345+
346+
<!--
347+
If you really can't wait, many features are already available on
348+
Rust [Nightly](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
349+
with `-Zunstable-options --edition=2021`.
350+
-->

0 commit comments

Comments
 (0)