Skip to content

Commit f5160f0

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

File tree

1 file changed

+346
-0
lines changed

1 file changed

+346
-0
lines changed

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

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

0 commit comments

Comments
 (0)