|
| 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—just like `println!()`—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—thanks to reserving these prefixes now—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. |
| 296 | +This enables you to 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. |
| 315 | +The name refers to its main use case: a pattern in a closure parameter. |
| 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 edition 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