- Feature Name: `ref_pat_eat_one_layer_2024`
- Start Date: (fill me in with today's date, YYYY-MM-DD)
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
- Rust Issue: [rust-lang/rust#123076](https://github.com/rust-lang/rust/issues/123076)
# Summary
[summary]: #summary
Various changes to the match ergonomics rules:
- On edition ≥ 2024, `&` and `&mut` patterns only remove a single layer of
references.
- On edition ≥ 2024, `mut` on an identifier pattern does not force its binding
mode to by-value.
- On edition ≥ 2024, `&` patterns can match against `&mut` references.
- On all editions, the binding mode can no longer ever be implicitly set to
`ref mut` behind an `&` pattern.
# Motivation
[motivation]: #motivation
Match ergonomics have been a great success overall, but there are some surprising
interactions that regularly confuse users.
- `mut` resets the binding mode to by-value, which users do not expect; the
mutability of the binding seems like a separate concern from its type
(<https://github.com/rust-lang/rust/issues/105647>,
<https://github.com/rust-lang/rust/issues/112545>)
- `&` and `&mut` patterns must correspond with a reference in the same position
in the scrutinee, even if there is an inherited reference present. Therefore,
users have no general mechanism to "cancel out" an inherited reference
(<https://users.rust-lang.org/t/reference-of-tuple-and-tuple-of-reference/91713/6>,
<https://users.rust-lang.org/t/cannot-deconstruct-reference-inside-match-on-reference-why/92147>,
<https://github.com/rust-lang/rust/issues/50008>,
<https://github.com/rust-lang/rust/issues/64586>)
- When an `&` or `&mut` pattern is used in a location where there is also an
inherited reference present, both are stripped.
# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation
Match ergonomics works a little differently in edition 2024 and above.
## Matching against inherited references
In all editions, when you match against an `&` or `&mut` reference with the type
of its referent, you get an "inherited reference": the binding mode of
"downstream" bindings is set to `ref` or `ref mut`.
```rust
// Unchanged from old editions:
// `x` "inherits" the `&` from the scrutinee type.
let [x] = &[42];
let _: &u8 = x;
```
In edition 2024 and above, an `&` or `&mut` pattern can match against this
inherited reference, consuming it. A pattern that does this has no other effect.
```rust
// New in edition 2023:
// `&` pattern consumes inherited `&` reference.
let [&x] = &[42];
let _: u8 = x;
```
## `&` matches against `&mut`
In edition 2024 and above, `&` patterns can match against `&mut` references
(including "inherited" references).
```rust
let &foo = &mut 42;
let _: u8 = foo;
```
## `mut` no longer strips the inherited reference
In older editions, `mut` on a binding "stripped" the inherited reference:
```rust
// Old editions
let (x, mut y) = &(true, false);
let _: (&bool, bool) = (x, y);
```
This no longer happens on edition ≥ 2024.
```rust
// Edition ≥ 2024
let (x, mut y) = &(true, false);
let _: (&bool, &bool) = (x, y);
```
# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation
This explanation assumes familiarity with the current match ergonomics rules,
including the "default binding mode" terminology. Refer to [RFC 2005](./2005-match-ergonomics.md#detailed-design).
## Edition 2024: `&` patterns can match against `&mut` references
`&` patterns can match against `&mut` references.
```rust
let &foo = &mut 42;
let _: u8 = foo;
```
However, the `ref mut` binding mode cannot be used behind such patterns.
```rust
let &ref mut foo = &mut 42;
// ^~ERROR: replace `&` with `&mut`
let _: &mut u8 = foo;
```
## Edition 2024: `&` and `&mut` can match against inherited references
When the default binding mode is `ref` or `ref mut`, `&` and `&mut` patterns can
reset it. `&` patterns will reset either `ref` or `ref mut` binding modes to
by-value, while `&mut` can only reset `ref mut`. An `&` or `&mut` pattern that
resets the binding mode in this way has no other effect.
```rust
let [&x] = &[3u8];
let _: u8 = x;
let [&mut x] = &mut [3u8];
let _: u8 = x;
let [&x] = &mut [3u8];
let _: u8 = x;
//let [&mut x] = &[3u8]; // ERROR
```
`&` patterns are otherwise unchanged from older editions.
```rust
let &a = &3;
let _: u8 = a;
//let &b = 17; // ERROR
```
If the default binding mode is `ref`, then `&mut` patterns are forbidden. If it
is by-value, then they have the same effect as on older editions.
```rust
//let [&mut x] = &[&mut 42]; // ERROR
//let &mut x = &&mut 3; // ERROR
// Unchanged from old editions
let &mut x = &mut 3;
let _: u8 = x;
let &mut x = &mut &mut 3;
let _: &mut u8 = x;
let &mut x = &mut &&mut 3;
let _: &&mut u8 = x;
```
## Edition 2024: `mut` does not reset binding mode to by-value
In the new edition, `mut` no longer resets the binding mode to by-value.
Therefore, it is possible to have a mutable by-reference binding. (An explicit
syntax for this is left to a future RFC.)
```rust
let &[mut a] = &[42];
a = &47;
```
## All editions: the default binding mode is never set to `ref mut` behind an `&` pattern or reference
The binding mode is set to `ref` instead in such cases. (On older editions, this
allows strictly more code to compile.)
```rust
// All editions
let &[[a]]; = &[&mut [42]];
let _: &u8 = a;
let &[[a]]; = &mut [&mut [42]];
let _: &u8 = a;
```
```rust
// Edition ≥ 2024
let &[[&a]]; = &[&mut [42]];
let _: u8 = a;
//let &[[&mut a]]; = &[&mut [42]]; // ERROR
```
# Migration
[migration]: #migration
This proposal, if adopted, would allow the same pattern to have different
meanings on different editions:
```rust
let [&a] = &[&0u8]; // `a` is `u8` on edition ≤ 2021, but `&u8` on edition ≥ 2024
let [mut a] = &[0u8]; // `a` is `u8` on edition ≤ 2021, but `&u8` on edition ≥ 2024
```
Instances of such incompatibilities are not very common, but far from unknown
(20 cases in `rustc`, for example). The migration lint for the feature entirely
desugars the match ergonomics of the affected pattern. This is necessary to
produce code that works on all editions, but it means that adopting the new
rules could require editing the affected patterns twice: once to desugar the
match ergonomics before adopting the new edition, and a second time to restore
match ergonomics after adoption of the new edition.
# Drawbacks
[drawbacks]: #drawbacks
This is a silent change in behavior, which is considered undesirable even
over an edition.
# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives
## Desirable property
[desirable-property]: #desirable-property
The proposed rules for new editions uphold the following property:
> For any two nested patterns `$pat0` and `$pat1`, such that `$pat1` uses match
> ergonomics only (no explicit `ref`/`ref mut`), and valid pattern match
> `let $pat0($pat1(binding)) = scrut`, either:
>
> - `let $pat0(temp) = scrut; let $pat1(binding) = temp;` compiles, with the
> same meaning as the original composed pattern match; or
> - `let $pat0(temp) = scrut; let $pat1(binding) = temp;` does not compile, but
> `let $pat0(ref temp) = scrut; let &$pat1(binding) = temp;` compiles, with the
> same meaning as the original composed pattern match.
In other words, the new match ergonomics rules are compositional.
## `&` patterns matching against `&mut`
There are several motivations for allowing this:
- It makes refactoring less painful. Sometimes, one is not certain whether an
unfinished API will end up returning a shared or a mutable reference. But as
long as the reference returned by said API is not actually used to perform
mutation, it often doesn't matter either way, as `&mut` implicitly reborrows
as `&` in many situations. Pattern matching is currently one of the most
prominent exceptions to this, and match ergonomics magnifies the pain because
a reference in one part of the pattern can affect the binding mode in a
different, faraway location[^nrmba]. If patterns can be written to always use
`&` unless mutation is required, then the amount of editing necessary to
perform various refactors is lessened.
- It's intuitive. `&mut` is strictly more powerful than `&`. It's conceptually a
subtype, and even if not implemented that way[^sub], coercions mean it often
feels like one in practice.
```rust
let a: &u8 = &mut 42;
```
[^nrmba]: This is even more true in light of the new rule that prevents the
default binding mode from being set to `ref mut` behind `&`.
[^sub]: Making `&mut` a subtype of `&` in actual implementation would require
adding significant complexity to the variance rules, but I do believe it to be
possible.
## `mut` not resetting the binding mode
Admittedly, there is not much use for mutable by-reference bindings. This is
true even outside of pattern matching; `let mut ident: &T = ...` is not commonly
seen (though not entirely unknown either). The motivation for making this change
anyway is that the current behavior is unintuitive and surprising for users.
## Versus "eat-two-layers"
An alternative proposal would be to allow `&` and `&mut` patterns to reset the
binding mode when not matching against a reference in the same position in the
scrutinee, but to not otherwise change their behavior. This would have the
advantage of not requiring an edition change. However, it would remain confusing
for users. Notably, the [property from earlier](#desirable-property) would
continue to not be satisfied.
In addition, this approach would lead to tricky questions around when
mutabilities should be considered compatible.
(This alternative *is* currently implemented, under its own feature gate.)
# Unresolved questions
[unresolved-questions]: #unresolved-questions
- How much churn will be necessary to adapt code for the new edition? There are
0 instances of affected patterns in the standard library, and 20 in the
compiler, but that is all the data we have at the moment.
# Future possibilities
[future-possibilities]: #future-possibilities
- An explicit syntax for mutable by-reference bindings will need to be chosen at
some point.
- Deref patterns may interact with `&` and `&mut` patterns.
- Future changes to reference types (partial borrows, language sugar for `Pin`,
etc) may interact with match ergonomics.
## Matching `&mut` behind `&`
There is one notable situation where match ergonomics cannot be used, and
explicit `ref` is required. Notably, this can occur where `&mut` is nested
behind `&`:
```rust
// No way to avoid the `ref` here
let &[&mut ref x] = &[&mut 42];
```
There are two strategies we could take to support this:
- `&mut` patterns could match "behind" `&`. For example, in `let [&mut x] = &[&mut 42];`,
the `&mut` pattern would match the `&mut` reference in the scrutinee, leaving
`&` to be inherited and resulting in `x: &i32`.
- The compiler could insert `&mut ref` in front of identifier patterns of type
`&mut` that are behind an `&` pattern. For example, `let &[x] = &[&mut 42];`
would be transformed into `let &[&mut ref x] = &[&mut 42];`. (There are some
subtleties around `@` patterns that would need to be resolved.)