We are pleased to announce that the third version of the Rust language, Rust 2021, is scheduled for release in October. Rust 2021 contains some small changes, but is expected to be a major improvement to the feel of Rust in practice.

What is a version?

The release of Rust 1.0 established “stability, not stagnation” as Rust’s core delivery value. Since the 1.0 release, the rule of Rust has been that once a feature is released to a stable release, we commit to supporting that feature in all future releases.

However, sometimes it is useful to be able to make small changes to the language that are not backward compatible. The most obvious example is the introduction of a new keyword, which invalidates variables with the same name. For example, the first version of Rust did not have the async and await keywords. Suddenly changing these words to keywords in later versions breaks things like let async = 1; .

Versioning is the mechanism we use to solve this problem. When we want to release a feature that would otherwise be backward incompatible, we make it part of a new Rust_ release. Versions are optional, so existing plates do not see these changes until they explicitly migrate to the new version. This means that even the latest version of Rust will still not use Async as a keyword unless you choose 2018 or later. This selection is made as part of _ each plate _ Cargo. Toml. New plates created by Cargo New are always configured to use the latest stable version.

Version is not going to split the ecosystem

The most important rule of versioning is that one version of Cockate can seamlessly interoperate with other versions of Cockate compiled. This ensures that the decision to move to a newer version is a “private decision” that can be made without affecting the other plates.

The requirement for crate interoperability means that we can make some restrictions in a release. Generally, the changes that occur in a release are “superficial”. All Rust code, regardless of version, is eventually compiled into the same internal representation in the compiler.

Version migration is easy and largely automated

Our goal is to make it easy to upgrade to a new version of Crack. We also provide tools to automate migration when we release new versions. It makes the small changes necessary to make your code compatible with the new version. For example, when migrating to Rust 2018, it changes anything called Async to use the equivalent primitive identifier syntax. R# async.

Automatic migration is not necessarily perfect: there may be corner cases that still need to be manually modified. The tool tries to avoid semantic changes that could affect the correctness or performance of the code.

In addition to instrumentalization, we maintain a version migration guide that covers changes to a version. This guide will describe the changes and give Pointers where people can learn more. It will also cover any corner cases or details that people should be aware of. This guide can be used both as an overview of versioning and as a quick troubleshooting reference when people encounter automated instrumentalization problems.

What are the changes to Rust 2021?


Over the past few months, the Rust 2021 Working Group has reviewed a number of proposals for what to include in the new release. We are pleased to announce the final list of version changes. Each feature must meet two criteria to get into this list. First, they must be approved by the relevant Rust team. Second, their implementation must go deep enough that we are confident they will be completed in time for the planned milestones.

A complement to the prelude

The prelude to the standard library is a module that contains everything imported automatically in each module. It contains common items such as Option, Vec, Drop, and Clone.

The Rust compiler gives preference to any manually imported projects over projects from preludes to ensure that the addition of preludes does not break any existing code. For example, if you have a block or module called example that contains a pub struct Option; Use example::*; Will make Option explicitly reference from example; Not from the standard library.

However, adding _ trait _ to the prelude breaks existing code in a subtle way. If STD ‘stryinto is also imported, a call to x.try_into() using the MyTryInto attribute might become ambiguous and uncompilable because it provides a method with the same name. That’s why we haven’t put TryInto in the prelude yet, because there’s a lot of code that breaks like this.

As a solution, Rust 2021 will use a new prelude. It is the same as the current prelude, but with three additions.

  • std::convert::TryInto
  • std::convert::TryFrom
  • std::iter::FromIterator

The default Cargo feature parser

Cargo has provided selective support for a new feature resolver since Rust 1.51.0, available via resolver = “2” in Cargo. Toml.

Starting with Rust 2021, this will be the default. That is, writing edition = “2021” in Cargo. Toml would mean resolver = “2”.

The new feature parser no longer incorporates features of all requests from Crates that depend on it in many ways. See Rust 1.51’s notice for details.

IntoIterator for arrays

Until Rust 1.53, only _ references _ to arrays were implemented IntoIterator. This means you can be in &[1, 2, 3] and &mut [1, 2, 3], but not directly in [1, 2, 3].

for &e in &[1, 2, 3] {} // Ok :)

for e in [1, 2, 3] {} // Error :(
Copy the code

It’s a long-standing problem, but the solution isn’t as simple as it seems. Simply adding a trait implementation breaks existing code. Today, array.into_iter() is ready to compile because it implicitly calls (&array).into_iter() according to how the method call syntax works. Adding a trait implementation changes its meaning.

Generally we classify this type of breaking (adding feature implementations) as “minor” and acceptable. But in this case, too much code can be broken.

It has been suggested several times that “only implement IntoIterator for arrays in Rust 2021”. However, this is simply not possible. You can’t have an implementation of a trait in one version and not in another, because versions can be mixed.

Instead, we decided to add a trait implementation (starting with Rust 1.53.0) to the _ all _ release, but to add a small hack to avoid failure before Rust 2021. In Rust 2015 and 2018 code, the compiler still resolves array.into_iter() to (&array).into_iter() as before, as if the trait implementation did not exist. This _ _ only applies to.into_iter() method call syntax. It does not affect any other syntax, such as for e in [1, 2, 3], ite.zip ([1, 2, 3]) or IntoIterator::into_iter([1, 2, 3]). These will start working in _ all _ releases.

While it’s a shame that it takes a small hack to avoid destruction, we’re very satisfied that this solution keeps the differences between versions to an absolute minimum. Because this hack only exists in the old version, there is no added complexity in the new version.

Discontinuous capture in closures

Closures automatically capture anything you reference in their body. For example, | | a + 1 will automatically capture for a reference in the surrounding environment.

For now, this applies to the entire structure, even if only one field is used. For example, | | a.x + 1 capture for a reference, rather than just a.x. In some cases, this is a problem. When one of the structure’s fields has been borrowed (mutable) or moved out, the other fields can no longer be used in the closure because this captures the entire structure, which is no longer available.

let a = SomeStruct::new(); drop(a.x); // Move out of one field of the struct println! ("{}", a.y); // Ok: Still use another field of the struct let c = || println! ("{}", a.y); // Error: Tries to capture all of `a` c();Copy the code

Starting with Rust 2021, closures will only capture the fields they use. Therefore, the above example compiles properly in Rust 2021.

This new behavior is only enabled in the new release because it can change the order in which fields are discarded. As for all version changes, it is important to note that automatic migration is available, which will update your closure. It can insert let _ = &a inside the closure; , forcing the entire structure to be captured as before.

panic! (a)Macro consistency

panic! Macros are one of Rust’s best-known macros. However, it has some subtle surprises that we can’t change casually due to backward compatibility.

panic! (" {} ", 1); // Ok, panics with the message "1" panic! (" {} "); // Ok, panics with the message "{}"Copy the code

panic! The () macro uses string formatting only when calling more than one argument. When a parameter is called, it doesn’t even look at that parameter.

let a = "{"; println! (a); // Error: First argument must be a format string literal panic! (a); // Ok: The panic macro doesn't careCopy the code

(It even accepts non-strings, such as Panic! (123), which is uncommon and rarely useful).

This is a particular problem once implicit format parameters are stabilized. This feature will make println! (“hello {name}”) becomes println! (” Hello {}”, name). However, panic! (” Hello {name}”) will not work as expected because panic! () does not treat individual arguments as format strings.

To avoid this confusion, Rust 2021 adopts the more consistent Panic! () macro. A new panic! () macros will no longer accept arbitrary expressions as their only arguments. It will be and println! Always treat the first argument as a format string. Due to panic! () will no longer accept arbitrary payloads. Panic_any () would be the only way to panic with anything other than a formatted string.

Also, in Rust 2021, Core :: Panic! () and STD: : panic! Theta is going to be the same. Currently, there are some historical differences between the two, in turning on or off #! When [NO_std], there is a significant difference.

Keep the grammar

To make room for some new syntax in the future, we decided to preserve the syntax for prefix#identifier,prefix”string”,prefix’c’, and prefix#123, where prefix can be any identifier. (Except for those that already make sense, such as B ‘… ‘and r “…” .

This is a breakthrough because macros currently accept Hello “world”, which they treat as two separate tags: Hello and “world”. However, the (automatic) fix is simple. Just insert a space: Hello “world”.

Besides turning these into toggling errors, the RFC has not attached meaning to any prefixes. Assigning meanings to specific prefixes is reserved for future suggestions, and since these prefixes are retained now, there will be no destructive changes.

These are some of the new prefixes you might see in the future.

  • F “”, short for a format string. For example, f”hello {name}”, as the equivalent of format_args! The abbreviation for the call to ().

  • C “” or Z “” represents an empty-tailed C string.

  • K# keyword to allow writing of keywords that do not yet exist in the current version. For example, while async is not a keyword in the 2015 release, this prefix will allow us to accept k#async as an alternative in the 2015 release while we wait for the 2018 release to keep async as a keyword.

Upgrade both warnings to hard errors

In Rust 2021, two existing lines will become hard errors. These lines are still warnings in older versions.

  • Bare-trait – Objects: Using the DYn keyword to identify trait objects will be mandatory in Rust 2021.

  • Ellipsis -inclusive-range-patterns: Rust 2021 no longer accepts discarded… This syntax is used for inclusive scope schemas. It has been.. =, which is consistent with expressions.

Or patterns in macro_rules

Starting from Rust 1.53.0, model is extended to support |, nested in any parts of the model. This makes | 2 (1), you can write instead of Some (1) | Some (2). Since this was never allowed before, this is not a breakthrough.

However, this change also affects the macro_rules macro. This macro accepts patterns using the: PAT fragment specifier. , at present: pat | _ _ not matching, because the Rust before 1.53, not all of the model (in all of the nested layer) can contain a |. To accept such A | B mode of macro, such as matches! () $($_ : pat) | + because we don’t want to destroy any existing macro, so we don’t have _ _ in the Rust 1.53.0 change: in the meaning of pat to include |.

Instead, we will make the modifications as part of Rust 2021. In the new version: pat fragment specify matching A | B will _ _.

Because sometimes people still hope that in the absence of | matching single mode variant, so increased the specified: pat_param, to retain the old behavior. The name refers to its main use: enclosing patterns in parameters.

What’s next?

Our plan is to complete the consolidation and full testing of these changes by September to ensure that the 2021 release is ready for Rust 1.56.0.

Note, however, that Rust is a volunteer-run project. We prioritize the personal well-being of everyone working on Rust over any deadlines and expectations we may set. This might mean delaying a release if necessary, or dropping a feature that proved too difficult or stressful to complete in time.

That said, we are on schedule and many of the difficult issues have been resolved, thanks to everyone who contributed to Rust 2021! 💛


You can expect another announcement about the new version in July. At that point, we expect all changes and automatic migrations to be implemented and ready for public testing.

We’ll be Posting some details about this process and the rejected suggestions soon on the Inside Rust blog.

If you really can’t wait, many features are already available on Rust Nightly with ‘-zunstable -options –edition=2021’.