Skip to content

Commit 4f29a65

Browse files
committed
Add chapter for 2024 match ergonomics reservations
We're reserving some syntax in patterns in Rust 2024. Let's add a chapter to describe that.
1 parent f56c9ae commit 4f29a65

File tree

2 files changed

+130
-4
lines changed

2 files changed

+130
-4
lines changed

src/SUMMARY.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
- [RPIT lifetime capture rules](rust-2024/rpit-lifetime-capture.md)
4444
- [`if let` temporary scope](rust-2024/temporary-if-let-scope.md)
4545
- [Tail expression temporary scope](rust-2024/temporary-tail-expr-scope.md)
46-
- [Match ergonomics](rust-2024/match-ergonomics.md)
46+
- [Match ergonomics reservations](rust-2024/match-ergonomics.md)
4747
- [Unsafe `extern` blocks](rust-2024/unsafe-extern.md)
4848
- [Unsafe attributes](rust-2024/unsafe-attributes.md)
4949
- [`unsafe_op_in_unsafe_fn` warning](rust-2024/unsafe-op-in-unsafe-fn.md)

src/rust-2024/match-ergonomics.md

+129-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,135 @@
1-
# Match ergonomics
2-
3-
**This is a placeholder, docs coming soon!**
1+
# Match ergonomics reservations
42

53
## Summary
64

5+
- Writing `mut`, `ref`, or `ref mut` on a binding is only allowed within a pattern when the pattern leading up to that binding is fully explicit (i.e. when it does not use match ergonomics).
6+
- Put differently, when the default binding mode is not `move`, writing `mut`, `ref`, or `ref mut` on a binding is an error.
7+
- Reference patterns (`&` or `&mut`) are only allowed within the fully-explicit prefix of a pattern.
8+
- Put differently, reference patterns can only match against references in the scrutinee when the default binding mode is `move`.
9+
710
## Details
811

12+
### Background
13+
14+
Within `match`, `let`, and other constructs, we match a *pattern* against a *scrutinee*. E.g.:
15+
16+
```rust
17+
let &[&mut [ref x]] = &[&mut [()]]; // x: &()
18+
// ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~
19+
// Pattern Scrutinee
20+
```
21+
22+
Such a pattern is called fully explicit because it does not elide (i.e. "skip" or "pass") any references within the scrutinee. By contrast, this otherwise-equivalent pattern is not fully explicit:
23+
24+
```rust
25+
let [[x]] = &[&mut [()]]; // x: &()
26+
```
27+
28+
Patterns such as this are said to be using match ergonomics, originally introduced in [RFC 2005][].
29+
30+
Under match ergonomics, as we incrementally match a pattern against a scrutinee, we keep track of the default binding mode. This mode can be one of `move`, `ref mut`, or `ref`, and it starts as `move`. When we reach a binding, unless an explicit binding mode is provided, the default binding mode is used to decide the binding's type.
31+
32+
For example, here we provide an explicit binding mode, causing `x` to be bound by reference:
33+
34+
```rust
35+
let ref x = (); // &()
36+
```
37+
38+
By contrast:
39+
40+
```rust
41+
let [x] = &[()]; // &()
42+
```
43+
44+
Here, in the pattern, we pass the outer shared reference in the scrutinee. This causes the default binding mode to switch from `move` to `ref`. Since there is no explicit binding mode specified, the `ref` binding mode is used when binding `x`.
45+
46+
[RFC 2005]: https://github.com/rust-lang/rfcs/pull/2005
47+
48+
### `mut` restriction
49+
50+
In Rust 2021 and earlier editions, we allow this oddity:
51+
52+
```rust
53+
let [x, mut y] = &[(), ()]; // x: &(), mut y: ()
54+
```
55+
56+
Here, because we pass the shared reference in the pattern, the default binding mode switches to `ref`. But then, in these editions, writing `mut` on the binding resets the default binding mode to `move`.
57+
58+
This can be surprising as it's not intuitive that mutability should affect the type.
59+
60+
To leave space to fix this, in Rust 2024 it's an error to write `mut` on a binding when the default binding mode is not `move`. That is, `mut` can only be written on a binding when the pattern (leading up to that binding) is fully explicit.
61+
62+
In Rust 2024, we can write the above example as:
63+
64+
```rust
65+
let &[ref x, mut y] = &[(), ()]; // x: &(), mut y: ()
66+
```
67+
68+
### `ref` / `ref mut` restriction
69+
70+
In Rust 2021 and earlier editions, we allow:
71+
72+
```rust
73+
let [ref x] = &[()]; // x: &()
74+
```
75+
76+
Here, the `ref` explicit binding mode is redundant, as by passing the shared reference (i.e. not mentioning it in the pattern), the binding mode switched to `ref`.
77+
78+
To leave space for other language possibilities, we are disallowing explicit binding modes where they are redundant in Rust 2024. We can rewrite the above example as simply:
79+
80+
```rust
81+
let [x] = &[()]; // x: &()
82+
```
83+
84+
### Reference patterns restriction
85+
86+
In Rust 2021 and earlier editions, we allow this oddity:
87+
88+
```rust
89+
let [&x, y] = &[&(), &()]; // x: (), y: &&()
90+
```
91+
92+
Here, the `&` in the pattern both matches against the reference on `&()` and resets the default binding mode to `move`. This can be surprising because the single `&` in the pattern causes a larger than expected change in the type by removing both layers of references.
93+
94+
To leave space to fix this, in Rust 2024 it's an error to write `&` or `&mut` in the pattern when the default binding mode is not `move`. That is, `&` or `&mut` can only be written when the pattern (leading up to that point) is fully explicit.
95+
96+
In Rust 2024, we can write the above example as:
97+
98+
```rust
99+
let &[&x, ref y] = &[&(), &()];
100+
```
101+
9102
## Migration
103+
104+
The [`rust_2024_incompatible_pat`][] lint flags patterns that are not allowed in Rust 2024. This lint is part of the `rust-2024-compatibility` lint group which is automatically applied when running `cargo fix --edition`. This lint will automatically convert affected patterns to fully explicit patterns that work correctly in Rust 2024 and in all prior editions.
105+
106+
To migrate your code to be compatible with Rust 2024, run:
107+
108+
```sh
109+
cargo fix --edition
110+
```
111+
112+
For example, this will convert this...
113+
114+
```rust
115+
let [x, mut y] = &[(), ()];
116+
let [ref x] = &[()];
117+
let [&x, y] = &[&(), &()];
118+
```
119+
120+
...into this:
121+
122+
```rust
123+
let &[ref x, mut y] = &[(), ()];
124+
let &[ref x] = &[()];
125+
let &[&x, ref y] = &[&(), &()];
126+
```
127+
128+
Alternatively, you can manually enable the lint to find patterns that will need to be migrated:
129+
130+
```rust
131+
// Add this to the root of your crate to do a manual migration.
132+
#![warn(rust_2024_incompatible_pat)]
133+
```
134+
135+
[`rust_2024_incompatible_pat`]: ../../rustc/lints/listing/allowed-by-default.html#rust-2024-incompatible-pat

0 commit comments

Comments
 (0)