Repository: rust
Issue: Fix discrepancy between match ergonomics Rule 4 and implementation
Labels: T-lang,C-discussion

In our 2024-06-26 design meeting on match ergonomics (part 3):

- https://github.com/rust-lang/lang-team/issues/268

...we decided on a slate on rules:

- **Rule 1**: When the DBM (default binding mode) is not `move` (whether or not behind a reference), writing `mut` on a binding is an error.
- **Rule 2**: When a reference pattern matches against a reference, do not update the DBM.
- **Rule 3**: If we've previously matched against a shared reference in the scrutinee (or against a `ref` DBM under *Rule 4*, or against a mutable reference treated as a shared one or a `ref mut` DBM treated as a `ref` one under *Rule 5*), set the DBM to `ref` whenever we would otherwise set it to `ref mut`.
- **Rule 4**: If an `&` or `&mut` pattern is being matched against a non-reference type **and if** the DBM is `ref` or `ref mut`, match the pattern against the DBM as though it were a type.
- **Rule 5**: If an `&` pattern is being matched against a mutable reference type (or against a `ref mut` DBM under *Rule 4*), act as if the type were a shared reference instead (or that the `ref mut` DBM is a `ref` DBM instead).

We then, in:

- https://github.com/rust-lang/rust/issues/127257

...amended *Rule 4* to read as follows:

- **Rule 4**: If an `&` pattern is being matched against a non-reference type *or an `&mut` pattern is being matched against a shared reference type* or a non-reference type, **and if** the DBM is `ref` or `ref mut`, match the pattern against the DBM as though it were a type.

(*Emphasis* highlights the diff.)

We did that so that it would accept cases like this:

```rust
let [&mut x] = &mut [&T]; //~ x: &T
```

It had been proposed the amended *Rule 4* was equivalent to an earlier proposal to accept the *union* of the code accepted under either the original *Rule 4* (the "structural" variant) or under *Rule 4 (early)* (the "early" variant).  (*Rule 4 (early)* breaks the primacy of structural matching by matching *first* against the DBM.)

However, in discussion, @WaffleLapkin found a case that shows that the amended *Rule 4* isn't equivalent to accepting the *union*:

```rust
let [&&mut x] = &[&mut T];
```

This case is accepted by the *early* variant but not by the amended *Rule 4*.

However, this *is* accepted by the implementation by @Jules-Bertholet, as merged in:

- https://github.com/rust-lang/rust/pull/127369

We could adjust the implementation to match the accepted RFC.

Or, conceivably, we could amend the statement of *Rule 4* to align with the implementation.  If we did that, the re-revised statement of *Rule 4* then would be:

- **Rule 4 (extended 2)**: If an `&` pattern is being matched against *a mutable reference type or* a non-reference type, or if an `&mut` pattern is being matched against a shared reference type or a non-reference type, **and if** the DBM is `ref` or `ref mut`, match the pattern against the DBM as though it were a type.

(*Emphasis* highlights the diff.)

Note that this would have the effect of taking precedence over *Rule 5*.

However, this too doesn't accept *strictly more* code.  Because under the originally-amended *Rule 4*, we accept this:

```rust
let [&x] = &[&mut T]; //~ x: &T
```

...but under the *extended 2* rule, that would be rejected:

```rust
let [&x] = &[&mut T]; //~ x: &mut T
//~^ ERROR cannot move out of non-copy array
```

We'll analyze this further, then nominate if we decide that the rule should be changed rather than the implementation.  We're not really worried about it; we see a number of acceptable options here (including implementing the *Rule 4* as written).

(If it were to come to it, we don't need to stabilize *Rule 4* immediately.  We only need to stabilize *Rule 1* and *Rule 2* in Rust 2024.)

Tracking:

- https://github.com/rust-lang/rust/issues/123076

@rustbot labels +T-lang

cc @Jules-Bertholet @Nadrieril