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

Add edition admonitions #1764

Merged
merged 2 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
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
10 changes: 8 additions & 2 deletions docs/authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ When assigning rules to new paragraphs, or when modifying rule names, use the fo
* If the rule is naming a specific Rust language construct (e.g. an attribute, standard library type/function, or keyword-introduced concept), use the construct as named in the language, appropriately case-adjusted (but do not replace `_`s with `-`s).
* Other than Rust language concepts with `_`s in the name, use `-` characters to separate words within a "subrule".
* Whenever possible, do not repeat previous components of the rule.
* Edition differences admonitions should typically be named by the edition referenced directly by the rule. If multiple editions are named, use the one for which the behavior is defined by the admonition, and not by a previous paragraph.
* Edition differences admonitions should typically be named by the edition where the behavior changed. You should be able to correspond the dates to the chapters in <https://doc.rust-lang.org/edition-guide/>.
* Target specific admonitions should typically be named by the least specific target property to which they apply (e.g. if a rule affects all x86 CPUs, the rule name should include `x86` rather than separately listing `i586`, `i686` and `x86_64`, and if a rule applies to all ELF platforms, it should be named `elf` rather than listing every ELF OS).
* Use an appropriately descriptive, but short, name if the language does not provide one.

Expand Down Expand Up @@ -197,4 +197,10 @@ The reference does not document which targets exist, or the properties of specif

### Editions

The main text and flow should document only the current edition. Whenever there is a difference between editions, the differences should be called out with an "Edition differences" block.
The main text and flow should document only the current edition. Whenever there is a difference between editions, the differences should be called out with an edition block, such as:

```markdown
r[foo.bar.edition2021]
> [!EDITION-2021]
> Describe what changed in 2021.
```
12 changes: 12 additions & 0 deletions mdbook-spec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,18 @@ impl Spec {
let blockquote = &caps["blockquote"];
let initial_spaces = blockquote.chars().position(|ch| ch != ' ').unwrap_or(0);
let space = &blockquote[..initial_spaces];
if lower.starts_with("edition-") {
let edition = &lower[8..];
return format!("{space}<div class=\"alert alert-edition\">\n\
\n\
{space}> <p class=\"alert-title\">\
<span class=\"alert-title-edition\">{edition}</span> Edition differences</p>\n\
{space} >\n\
{blockquote}\n\
\n\
{space}</div>\n");
}

// These icons are from GitHub, MIT License, see https://github.com/primer/octicons
let svg = match lower.as_str() {
"note" => "<path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path>",
Expand Down
9 changes: 6 additions & 3 deletions src/abi.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ with the same name (or with a well-known symbol), leading to undefined behavior.
extern "C" fn foo() {}
```

> **Edition differences**: Before the 2024 edition it is allowed to use the `no_mangle` attribute without the `unsafe` qualification.
> [!EDITION-2024]
> Before the 2024 edition it is allowed to use the `no_mangle` attribute without the `unsafe` qualification.

## The `link_section` attribute

Expand All @@ -92,7 +93,8 @@ of memory not expecting them, such as mutable data into read-only areas.
pub static VAR1: u32 = 1;
```

> **Edition differences**: Before the 2024 edition it is allowed to use the `link_section` attribute without the `unsafe` qualification.
> [!EDITION-2024]
> Before the 2024 edition it is allowed to use the `link_section` attribute without the `unsafe` qualification.

## The `export_name` attribute

Expand All @@ -109,7 +111,8 @@ behavior.
pub fn name_in_rust() { }
```

> **Edition differences**: Before the 2024 edition it is allowed to use the `export_name` attribute without the `unsafe` qualification.
> [!EDITION-2024]
> Before the 2024 edition it is allowed to use the `export_name` attribute without the `unsafe` qualification.

[_MetaNameValueStr_]: attributes.md#meta-item-attribute-syntax
[`static` items]: items/static-items.md
Expand Down
3 changes: 2 additions & 1 deletion src/destructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ smallest scope that contains the expression and is one of the following:
> The [scrutinee] of a `match` expression is not a temporary scope, so temporaries in the scrutinee can be dropped after the `match` expression. For example, the temporary for `1` in `match 1 { ref mut z => z };` lives until the end of the statement.

r[destructors.scope.temporary.edition2024]
> **Edition differences**: The 2024 edition added two new temporary scope narrowing rules: `if let` temporaries are dropped before the `else` block, and temporaries of tail expressions of blocks are dropped immediately after the tail expression is evaluated.
> [!EDITION-2024]
> The 2024 edition added two new temporary scope narrowing rules: `if let` temporaries are dropped before the `else` block, and temporaries of tail expressions of blocks are dropped immediately after the tail expression is evaluated.

Some examples:

Expand Down
4 changes: 3 additions & 1 deletion src/expressions/await-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ More specifically, an await expression has the following effect.
5. If the call to `poll` returns [`Poll::Pending`], then the future returns `Poll::Pending`, suspending its state so that, when the surrounding async context is re-polled,execution returns to step 3;
6. Otherwise the call to `poll` must have returned [`Poll::Ready`], in which case the value contained in the [`Poll::Ready`] variant is used as the result of the `await` expression itself.

> **Edition differences**: Await expressions are only available beginning with Rust 2018.
r[expr.await.edition2018]
> [!EDITION-2018]
> Await expressions are only available beginning with Rust 2018.

r[expr.await.task]
## Task context
Expand Down
4 changes: 3 additions & 1 deletion src/expressions/block-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ The actual data format for this type is unspecified.
> [!NOTE]
> The future type that rustc generates is roughly equivalent to an enum with one variant per `await` point, where each variant stores the data needed to resume from its corresponding point.

> **Edition differences**: Async blocks are only available beginning with Rust 2018.
r[expr.block.async.edition2018]
> [!EDITION-2018]
> Async blocks are only available beginning with Rust 2018.

r[expr.block.async.capture]
### Capture modes
Expand Down
3 changes: 2 additions & 1 deletion src/expressions/closure-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ async fn example() {
```

r[expr.closure.async.edition2018]
> **Edition differences**: Async closures are only available beginning with Rust 2018.
> [!EDITION-2018]
> Async closures are only available beginning with Rust 2018.

## Example

Expand Down
4 changes: 3 additions & 1 deletion src/expressions/method-call-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ r[expr.method.ambiguous-search]
If a step is reached where there is more than one possible method, such as where generic methods or traits are considered the same, then it is a compiler error.
These cases require a [disambiguating function call syntax] for method and function invocation.

> **Edition differences**: Before the 2021 edition, during the search for visible methods, if the candidate receiver type is an [array type], methods provided by the standard library [`IntoIterator`] trait are ignored.
r[expr.method.edition2021]
> [!EDITION-2021]
> Before the 2021 edition, during the search for visible methods, if the candidate receiver type is an [array type], methods provided by the standard library [`IntoIterator`] trait are ignored.
>
> The edition used for this purpose is determined by the token representing the method name.
>
Expand Down
5 changes: 3 additions & 2 deletions src/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ These conventions are documented here.

An *example term* is an example of a term being defined.

* Differences in the language by which edition the crate is compiled under are in a blockquote that start with the words "Edition differences:" in **bold**.
* The main text describes the latest stable edition. Differences to previous editions are separated in edition blocks:

> **Edition differences**: In the 2015 edition, this syntax is valid that is disallowed as of the 2018 edition.
> [!EDITION-2018]
> In the 2015 edition, this syntax is valid that is disallowed as of the 2018 edition.

* Notes that contain useful information about the state of the book or point out useful, but mostly out of scope, information are in note blocks.

Expand Down
7 changes: 3 additions & 4 deletions src/items/associated-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,9 @@ let circle_shape = Circle::new();
let bounding_box = circle_shape.bounding_box();
```

r[items.associated.fn.params.edition2015]
> **Edition differences**: In the 2015 edition, it is possible to declare trait
> methods with anonymous parameters (e.g. `fn foo(u8)`). This is deprecated and
> an error as of the 2018 edition. All parameters must have an argument name.
r[items.associated.fn.params.edition2018]
> [!EDITION-2018]
> In the 2015 edition, it is possible to declare trait methods with anonymous parameters (e.g. `fn foo(u8)`). This is deprecated and an error as of the 2018 edition. All parameters must have an argument name.

r[items.associated.fn.param-attributes]
#### Attributes on method parameters
Expand Down
3 changes: 2 additions & 1 deletion src/items/external-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ r[items.extern.unsafe-required]
The `unsafe` keyword is semantically required to appear before the `extern` keyword on external blocks.

r[items.extern.edition2024]
> **Edition differences**: Prior to the 2024 edition, the `unsafe` keyword is optional. The `safe` and `unsafe` item qualifiers are only allowed if the external block itself is marked as `unsafe`.
> [!EDITION-2024]
> Prior to the 2024 edition, the `unsafe` keyword is optional. The `safe` and `unsafe` item qualifiers are only allowed if the external block itself is marked as `unsafe`.

r[items.extern.fn]
## Functions
Expand Down
4 changes: 2 additions & 2 deletions src/items/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ For more information on the effect of async, see [`async` blocks][async-blocks].
[`impl Future`]: ../types/impl-trait.md

r[items.fn.async.edition2018]
> **Edition differences**: Async functions are only available beginning with
> Rust 2018.
> [!EDITION-2018]
> Async functions are only available beginning with Rust 2018.

r[items.fn.async.safety]
### Combining `async` and `unsafe`
Expand Down
17 changes: 9 additions & 8 deletions src/items/use-declarations.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ fn example() {
}
```

r[items.use.path.edition2015]
> **Edition differences**: In the 2015 edition, `use` paths are relative to the crate root.
> For example:
r[items.use.path.edition2018]
> [!EDITION-2018]
> In the 2015 edition, `use` paths are relative to the crate root. For example:
>
> ```rust,edition2015
> mod foo {
Expand Down Expand Up @@ -196,8 +196,9 @@ r[items.use.multiple-syntax.empty]
An empty brace does not import anything, though the leading path is validated that it is accessible.
<!-- This is slightly wrong, see: https://github.com/rust-lang/rust/issues/61826 -->

r[items.use.multiple-syntax.edition2015]
> **Edition differences**: In the 2015 edition, paths are relative to the crate root, so an import such as `use {foo, bar};` will import the names `foo` and `bar` from the crate root, whereas starting in 2018, those names are relative to the current scope.
r[items.use.multiple-syntax.edition2018]
> [!EDITION-2018]
> In the 2015 edition, paths are relative to the crate root, so an import such as `use {foo, bar};` will import the names `foo` and `bar` from the crate root, whereas starting in 2018, those names are relative to the current scope.

r[items.use.self]
## `self` imports
Expand Down Expand Up @@ -307,9 +308,9 @@ r[items.use.glob.last-segment-only]
r[items.use.glob.self-import]
`*` cannot be used to import a module's contents into itself (such as `use self::*;`).

r[items.use.glob.edition2015]
> **Edition differences**: In the 2015 edition, paths are relative to the crate root, so an import such as `use *;` is valid, and it means to import everything from the crate root.
> This cannot be used in the crate root itself.
r[items.use.glob.edition2018]
> [!EDITION-2018]
> In the 2015 edition, paths are relative to the crate root, so an import such as `use *;` is valid, and it means to import everything from the crate root. This cannot be used in the crate root itself.

r[items.use.as-underscore]
## Underscore Imports
Expand Down
11 changes: 7 additions & 4 deletions src/macros-by-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,16 @@ The keyword metavariable `$crate` can be used to refer to the current crate; see
transcribed more than once or not at all.

r[macro.decl.meta.edition2021]
> **Edition differences**: Starting with the 2021 edition, `pat` fragment-specifiers match top-level or-patterns (that is, they accept [_Pattern_]).
> [!EDITION-2021]
> Starting with the 2021 edition, `pat` fragment-specifiers match top-level or-patterns (that is, they accept [_Pattern_]).
>
> Before the 2021 edition, they match exactly the same fragments as `pat_param` (that is, they accept [_PatternNoTopAlt_]).
>
> The relevant edition is the one in effect for the `macro_rules!` definition.

r[macro.decl.meta.edition2024]
> **Edition differences**: Before the 2024 edition, `expr` fragment specifiers do not match [_UnderscoreExpression_] or [_ConstBlockExpression_] at the top level. They are allowed within subexpressions.
> [!EDITION-2024]
> Before the 2024 edition, `expr` fragment specifiers do not match [_UnderscoreExpression_] or [_ConstBlockExpression_] at the top level. They are allowed within subexpressions.
>
> The `expr_2021` fragment specifier exists to maintain backwards compatibility with editions before 2024.

Expand Down Expand Up @@ -496,7 +498,7 @@ macro_rules! call_foo {
fn foo() {}
```

> **Version & Edition differences**: Prior to Rust 1.30, `$crate` and
> **Version differences**: Prior to Rust 1.30, `$crate` and
> `local_inner_macros` (below) were unsupported. They were added alongside
> path-based imports of macros (described above), to ensure that helper macros
> did not need to be manually imported by users of a macro-exporting crate.
Expand Down Expand Up @@ -567,7 +569,8 @@ r[macro.decl.follow-set.token-other]
* All other fragment specifiers have no restrictions.

r[macro.decl.follow-set.edition2021]
> **Edition differences**: Before the 2021 edition, `pat` may also be followed by `|`.
> [!EDITION-2021]
> Before the 2021 edition, `pat` may also be followed by `|`.

r[macro.decl.follow-set.repetition]
When repetitions are involved, then the rules apply to every possible number of
Expand Down
14 changes: 5 additions & 9 deletions src/names/preludes.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,10 @@ r[names.preludes.extern.std]
The [`std`] crate is added as long as the [`no_std` attribute] is not specified in the crate root.

r[names.preludes.extern.edition2018]
> **Edition differences**: In the 2015 edition, crates in the extern prelude
> cannot be referenced via [use declarations], so it is generally standard
> practice to include `extern crate` declarations to bring them into scope.
> [!EDITION-2018]
> In the 2015 edition, crates in the extern prelude cannot be referenced via [use declarations], so it is generally standard practice to include `extern crate` declarations to bring them into scope.
>
> Beginning in the 2018 edition, [use declarations] can reference crates in
> the extern prelude, so it is considered unidiomatic to use `extern crate`.
> Beginning in the 2018 edition, [use declarations] can reference crates in the extern prelude, so it is considered unidiomatic to use `extern crate`.

> [!NOTE]
> Additional crates that ship with `rustc`, such as [`alloc`], and [`test`](mod@test), are not automatically included with the `--extern` flag when using Cargo. They must be brought into scope with an `extern crate` declaration, even in the 2018 edition.
Expand Down Expand Up @@ -155,10 +153,8 @@ r[names.preludes.no_implicit_prelude.lang]
This attribute does not affect the [language prelude].

r[names.preludes.no_implicit_prelude.edition2018]
> **Edition differences**: In the 2015 edition, the `no_implicit_prelude`
> attribute does not affect the [`macro_use` prelude], and all macros exported
> from the standard library are still included in the `macro_use` prelude.
> Starting in the 2018 edition, it will remove the `macro_use` prelude.
> [!EDITION-2018]
> In the 2015 edition, the `no_implicit_prelude` attribute does not affect the [`macro_use` prelude], and all macros exported from the standard library are still included in the `macro_use` prelude. Starting in the 2018 edition, it will remove the `macro_use` prelude.

[`extern crate`]: ../items/extern-crates.md
[`macro_use` attribute]: ../macros-by-example.md#the-macro_use-attribute
Expand Down
11 changes: 4 additions & 7 deletions src/paths.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,11 @@ Paths starting with `::` are considered to be *global paths* where the segments
start being resolved from a place which differs based on edition. Each identifier in
the path must resolve to an item.

r[paths.qualifiers.global-root.edition2015]
> **Edition Differences**: In the 2015 Edition, identifiers resolve from the "crate root"
> (`crate::` in the 2018 edition), which contains a variety of different items, including
> external crates, default crates such as `std` or `core`, and items in the top level of
> the crate (including `use` imports).
r[paths.qualifiers.global-root.edition2018]
> [!EDITION-2018]
> In the 2015 Edition, identifiers resolve from the "crate root" (`crate::` in the 2018 edition), which contains a variety of different items, including external crates, default crates such as `std` or `core`, and items in the top level of the crate (including `use` imports).
>
> Beginning with the 2018 Edition, paths starting with `::` resolve from
> crates in the [extern prelude]. That is, they must be followed by the name of a crate.
> Beginning with the 2018 Edition, paths starting with `::` resolve from crates in the [extern prelude]. That is, they must be followed by the name of a crate.

```rust
pub fn foo() {
Expand Down
9 changes: 6 additions & 3 deletions src/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,8 @@ let [ref mut x] = &mut [()]; //~ ERROR
```

r[patterns.ident.binding.mode-limitations.edition2024]
> **Edition differences**: Before the 2024 edition, bindings could explicitly specify a `ref` or `ref mut` binding mode even when the default binding mode was not "move", and they could specify mutability on such bindings with `mut`. In these editions, specifying `mut` on a binding set the binding mode to "move" regardless of the current default binding mode.
> [!EDITION-2024]
> Before the 2024 edition, bindings could explicitly specify a `ref` or `ref mut` binding mode even when the default binding mode was not "move", and they could specify mutability on such bindings with `mut`. In these editions, specifying `mut` on a binding set the binding mode to "move" regardless of the current default binding mode.

r[patterns.ident.binding.mode-limitations-reference]
Similarly, a reference pattern may only appear when the default binding mode is "move". For example, this is not accepted:
Expand All @@ -344,7 +345,8 @@ let [&x] = &[&()]; //~ ERROR
```

r[patterns.ident.binding.mode-limitations-reference.edition2024]
> **Edition differences**: Before the 2024 edition, reference patterns could appear even when the default binding mode was not "move", and had both the effect of matching against the scrutinee and of causing the default binding mode to be reset to "move".
> [!EDITION-2024]
> Before the 2024 edition, reference patterns could appear even when the default binding mode was not "move", and had both the effect of matching against the scrutinee and of causing the default binding mode to be reset to "move".

r[patterns.ident.binding.mixed]
Move bindings and reference bindings can be mixed together in the same pattern.
Expand Down Expand Up @@ -669,7 +671,8 @@ _RangeFromPattern_ cannot be used as a top-level pattern for subpatterns in [sli
For example, the pattern `[1.., _]` is not a valid pattern.

r[patterns.range.edition2021]
> **Edition differences**: Before the 2021 edition, range patterns with both a lower and upper bound may also be written using `...` in place of `..=`, with the same meaning.
> [!EDITION-2021]
> Before the 2021 edition, range patterns with both a lower and upper bound may also be written using `...` in place of `..=`, with the same meaning.

r[patterns.ref]
## Reference patterns
Expand Down
Loading