owned this note
owned this note
Published
Linked with GitHub
---
title: "Design meeting 2024-05-15: Match ergonomics 2024"
tags: ["T-lang", "design-meeting", "minutes"]
date: 2024-05-15
discussion: https://rust-lang.zulipchat.com/#narrow/stream/410673-t-lang.2Fmeetings/topic/Design.20meeting.202024-05-15
url: https://hackmd.io/9SstshpoTP60a8-LrxsWSA
---
# Review of current behavior (TC)
## Distribution
An outer reference is distributed to inner bindings:
```rust
let (a,) = &(A,);
-------------------- // a: &A
let (ref a,) = (A,);
```
## Reducing outer references
Multiple outer references are reduced to a single inner reference:
```rust
let (a,) = &&(A,);
-------------------- // a: &A
let (ref a,) = (A,);
```
## Preservation
Multiple inner references are preserved:
```rust
let (a,) = (&&A,);
--------------------- // a: &&A
let (ref a,) = (&A,);
```
Any remaining outer references are combined with the preserved inner references:
```rust
let (a,) = &(&A,);
--------------------- // a: &&A
let (ref a,) = (&A,);
```
## Reduction
An inner `&` in the pattern first removes any remaining outer reference, then removes an inner reference, and gives an error if there is not an inner reference to remove:
```rust
let (&a,) = &(&&A,);
-------------------- // a: &A
let (ref a,) = (A,);
```
```rust
let (&a,) = &(&A,);
------------------- // a: A
let (a,) = (A,);
```
```rust
let (&a,) = &(A,); //~ ERROR type mismatch
```
```rust
let (&a,) = (&&A,);
-------------------- // a: &A
let (ref a,) = (A,);
```
```rust
let (&a,) = (&A,);
------------------ // a: A
let (a,) = (A,);
```
```rust
let (&a,) = (A,); //~ ERROR type mismatch
```
## `mut` oddity
`mut` resets the binding mode. So:
```rust
let (mut a,) = &(&A,);
---------------------- // a: &A
let (ref a,) = (A,);
let mut a = a;
```
By contrast:
```rust
let (a,) = &(&A,);
--------------------- // a: &&A
let (ref a,) = (&A,);
```
## Distribution oddity
Even though:
```rust
let (a,) = &(A,);
-------------------- // a: &A
let (ref a,) = (A,);
```
This is an error:
```rust
let (&a,) = &(A,); //~ ERROR type mismatch
```
This is surprising because, without the `&` in the pattern, `a: &A`.
---
- Feature Name: `ref_pat_eat_one_layer_2024`
- Start Date: 2024-05-06
- RFC PR: [rust-lang/rfcs#3627](https://github.com/rust-lang/rfcs/pull/3627)
- 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
`mut` resets the binding mode to by-value, which users do not expect; the
mutability of the binding would seem to be separate concern from its type
(<https://github.com/rust-lang/rust/issues/105647>,
<https://github.com/rust-lang/rust/issues/112545>).
```rust
let (x, mut y) = &(true, false);
let _: (&bool, bool) = (x, y);
```
## Can't cancel out an inherited reference
`&` 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>).
```rust
fn foo(arg: &(String, Vec<i32>, u8)) {
// We want to extract `&String`, `&Vec`, and `u8` from the tuple.
let (s, v, u) = arg; // u is &u8, not what we wanted
let &(ref s, ref v, u) = arg; // we have to abandon match ergonomics entirely
}
```
## A single `&` can strip two references
When an `&` or `&mut` pattern is used in a location where there is also an
inherited reference present, both are stripped; adding a single `&` to the
pattern can remove two `&`s from the type of the binding.
```rust
let [a] = &[&42]; // a = &&42
let [&a] = &[&42]; // a = 42
```
# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation
Match ergonomics works a little differently in edition 2024 and above.
## `mut` no longer strips the inherited reference
`mut` on a binding does not reset the binding mode on edition ≥ 2024.
```rust
//! Edition ≥ 2024
let (x, mut y) = &(true, false);
let _: (&bool, &bool) = (x, y); // instead of `(&bool, bool)`
```
## 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
//! All 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
//! Edition ≥ 2024
// `&` pattern consumes inherited `&` reference.
let [&x] = &[42];
let _: u8 = x;
// Examples from motivation section
fn foo(arg: &(String, Vec<i32>, u8)) {
let (s, v, &u) = arg;
let _: (&String, &Vec<i32>, u8) = (s, v, u);
}
let [&x] = &[&42];
let _: &u8 = x;
```
## `&` matches against `&mut`
In edition 2024 and above, `&` patterns can match against `&mut` references
(including "inherited" references).
```rust
//! Edition ≥ 2024
let &foo = &mut 42;
let _: u8 = foo;
```
# 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: `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
//! Edition ≥ 2024
let [mut a] = &[42];
a = &47;
```
## Edition 2024: `&` patterns can match against `&mut` references
`&` patterns can match against `&mut` references.
```rust
//! Edition ≥ 2024
let &foo = &mut 42;
let _: u8 = foo;
```
However, the `ref mut` binding mode cannot be used behind such patterns.
```rust
//! All editions
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
//! Edition ≥ 2024
let [&x] = &[3u8];
let _: u8 = x;
let [&mut x] = &mut [3u8];
let _: u8 = x;
let [&x] = &mut [3u8];
let _: u8 = x;
```
```rust
//! All editions
//let [&mut x] = &[3u8]; // ERROR
```
`&` patterns are otherwise unchanged from older editions.
```rust
//! All editions
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
//! Edition ≥ 2024
//let [&mut x] = &[&mut 42]; // ERROR
```
```rust
//! All 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;
//let &mut x = &&mut 3; // ERROR
```
## 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 (new)
let &[[a]] = &[&mut [42]];
let _: &u8 = a; // previously `a` would be `&mut u8`, resulting in a move check error
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 appear to be uncommon, 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.
## Macro subpatterns
Unfortunately, when a subpattern derives from a macro expansion, fully
desugaring the match ergonomics may not be possible. For example:
```rust
//! crate foo (edition 2021)
#[macro_export]
macro_rules! foo {
($foo:ident) => {
[$foo]
};
}
```
```rust
//! crate bar (edition 2021, want to migrate to 2024)
extern crate foo;
use foo::*;
fn main() {
let ([&x], foo!(y)) = &([&0], [0]);
//~^ WARN: the semantics of this pattern will change in edition 2024
let _: i32 = x;
let _: &i32 = y;
}
```
In such cases, there is no possible machine-applicable suggstion we could emit
to produce code compatible with all editions (short of expanding the macro).
However, such code should be extremely rare in practice.
# 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 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.
## `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.
## Never setting default binding mode to `ref mut` behind `&`
### We can't delay this choice
Note that while this is not a breaking change for edition 2021 and below, it
*would be breaking* to adopt the rest of this RFC without this change, and then
later adopt this change alone. Specifically, pattern matches like the following
would break:
```rust
let &[[&mut a]] = &[&mut [42]];
// `&mut` in pattern needs to match against either:
// - `&mut` in value at same position (there is none, so not possible)
// - inherited `&mut` (which the "never set default binding mode to `ref mut` behind `&`" rule
// downgrades to `&`)
```
Therefore, we cannot delay a decision on this matter.
### Makes behavior more consistent
On all editions, when a structure pattern peels off a shared reference and the
default binding mode is already `ref mut`, the binding mode gets set to `ref`.
But when the binding mode is set to `ref`, and a mutable reference is peeled
off, the binding mode remains `ref`. In other words, immutability usually takes
precedence over mutability. This change, in addition to being generally useful,
makes the match ergonomics rules more consistent by ensuring that immutability
*always* takes precedence over mutability.
### Ensures that a desirable property is preserved
The current match ergonomics rules uphold the following desirable property:
> An `&mut` pattern is accepted if and only if removing the pattern would allow
> obtaining an `&mut` value.
For example:
```rust
//! All editions
let &mut a = &mut 42; // `a: i32`
let a = &mut 42; // `a: &mut i32`
let &[&mut a] = &[&mut 42]; // `a: i32`
//let &[a] = &[&mut 42]; // ERROR, but…
let &[ref a] = &[&mut 42]; // `a = &&mut i32` (so we did manage to obtain an `&mut i32` in some form)
```
Adopting the "no `ref mut` behind `&`" rule ensures that this property continues
to hold for edition 2024:
```rust
//! Edition ≥ 2024
let &[[&mut x]] = &[&mut [42]]; // If we were allow this, with `x: i32` …
//let &[[x]] = &[&mut [42]]; // remove the `&mut` → ERROR, if the default binding mode is to be `ref mut`
// nothing we do will get us `&mut i32` in any form
```
## `&` 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 especially true in light of the [new rule](#all-editions-the-default-binding-mode-is-never-set-to-ref-mut-behind-an--pattern-or-reference)
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.
## 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. And there would be compatibility
concerns with certain proposals for "deref patterns".
(This alternative is currently implemented under a separate 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 should be chosen at some
point.
- Future changes to reference types (partial borrows, language sugar for `Pin`,
etc) may interact with match ergonomics.
## Deref patterns
Because it is compositional, the "eat-one-layer" model proposed by this RFC is
fully compatible with proposals for "deref patterns", including allowing
`&`/`&mut` patterns to match against types implementing `Deref`/`DerefMut`. One
question that would need to be resolved is whether and how deref patterns
(explicit or implicit) affect the default binding mode.
## 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`, even with this RFC
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`.
- This may not extend gracefully to future language features (partial borrows,
for example) as it relies on reference types forming a total order.
- 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];`.
- The full desugaring would be more complicated, as it would need to handle
`@` patterns.
---
# Discussion
## Attendance
- People: TC, Josh, nikomatsakis, tmandry, pnkfelix, scottmcm, eholk, Jules Bertholet, Nadri
## Meeting roles
- Minutes, driver: TC
## Checkboxes?
Josh: LGTM, where do I check my box? :)
TC: Propose FCP here:
https://github.com/rust-lang/rfcs/pull/3627
Josh: I'll wait for someone else to start the FCP (because starting FCP prevents unchecking a box later). Happy to check a box once there's an FCP started though.
## Editions and macros
nikomatsakis: The text says
> In such cases, there is no possible machine-applicable suggstion we could emit to produce code compatible with all editions (short of expanding the macro). However, such code should be extremely rare in practice.
...but normally we set the edition behavior not based on the surrounding crate but based on the edition that wrote the code. In this case, I'd say that's the "edition of the pattern". The specific example ...
```rust
//! crate foo (edition 2021)
#[macro_export]
macro_rules! foo {
($foo:ident) => {
[$foo]
};
}
```
```rust
//! crate bar (edition 2021, want to migrate to 2024)
extern crate foo;
use foo::*;
fn main() {
let ([&x], foo!(y)) = &([&0], [0]);
//~^ WARN: the semantics of this pattern will change in edition 2024
let _: i32 = x;
let _: &i32 = y;
}
```
...is interesting. Conceivably the `[y]` pattern could continue to operate in "Rust 2021" mode, though I'm not sure if that's an entirely self-consistent notion.
Jules: It *does* operate in Rust 2021 mode, but it inherits the default binding mode from the Rust 2024 outer pattern. (But it doesn't actually matter here, as the two editions would assign `foo` the same semantics)
Josh: So, this isn't an issue for patterns that come entirely from 2021 or entirely from 2024, only an issue for patterns with mixed editions because they're combined from code inside the macro and code passed into the macro?
Jules: I would describe the issue as, to migrate a pattern from edition 2021 to 2024, you may need to edit parts of the entire pattern, but that's not possible if some of the pattern is hidden behind a macro whose definition you can't edit. (In this specific example, the macro remains on edition 2021,
but that's not necessarily true for every case.)
Nadri: for reference, the desugared pattern would have to be:
```rust
let &([&x], [ref y]) = &([&0], [0]);
```
Nadri: A solution to this problem would be to generalize `ref x` to `ref <pat>`. I think that would make it possible to write a migration lint that is local. The migration would here be:
```rust
let &([&x], ref foo!(y)) = &([&0], [0]);
```
Nadri: That's harder to implement and results in uglier code though.
## Just add `&`
tmandry: The RFC says that this compiles:
```rust!
let &[[&a]] = &[&mut [42]];
let _: u8 = a;
```
I want to know whether this compiles. In other words, if I see that my binding `a` has type `&u8`, can I always just throw another `&` in front of it to remove the reference?
```rust!
let [[&&a]] = &[&mut [42]];
let _: u8 = a;
```
Jules: Yes.
## Future explicit syntax
nikomatsakis: This appears in the future possibilities section...
> An explicit syntax for mutable by-reference bindings should be chosen at some
...I guess the point is that when you get the `&mut` "inherited" you can wind up with a mutable binding...
```rust
let [mut x] = &mut [22];
// in 2021: `mut x: i32` -- definitely surprising!
// in 2024: `mut x: &mut i32` -- proposed change
```
...but if you were to write...
```rust
let &mut [ref mut x] = &mut [22];
// x: &mut i32
let mut x = x;
```
...then the `x` would be immutable? Interesting. I missed that interaction at first.
Nadri: indeed, to desugar the original properly, we'd need to add something like `&mut [mut ref mut x]` or `&mut [ref mut (mut x)]`.
Nadri: this means that without these new binding modes, with this proposal it is no longer possible to desugar match ergonomics into valid rust syntax.
NM: I agree that the current behavior of `mut` is surprising, but I don't know how often it affects Rust learners in practice. I'm wondering if we should just make it an error and require an explicit `&` instead, so that we don't have the discontinuity here (i.e., there remains a "fully elaborated" form).
FK: I like elaborated forms.
TC: This is what was taken out of this RFC to defer the bikeshed.
NM: I'm glad syntax like `mut ref` and `mut ref mut` is not part of this proposal, because I think it will be very confusing for people.
FK: I'm happy to delay trying to figure out how to crack this nut.
tmandry: We're not reducing expressiveness here; we're just leaving a few gaps in expressiveness to work out later.
NM: The two questions are:
1. Does resetting the binding mode for `mut` matter? I agree we shouldn't have done it if we could go back in time.
2. Should we disallow a `mut` binding if there are inherited references? (e.g., line 705 would have to be `let &mut [x]` or `let [&mut (mut x)]` to preserve Rust 2021 semantics)
```rust
let &mut [ref mut x] = &mut [22];
// x: &mut i32
let mut x = x;
```
Jules: Agreed that this does not come up often, but it does come up and confuse people. There are issues linked in the motivation section related to this where people have been confused.
Jules: On the explicit syntax, I took it out to avoid the bikeshed. I'm happy to add it back in.
Jules: I agree with pnkfelix that the mutability of the binding and the mutability of the reference are separate concerns. If both aren't available, that doesn't seem great.
TC: Would making `mut` an error impact composionality property that you are trying to achieve, Jules?
Jules/tmandry: It shouldn't, because this would make code not compile, and the compositionality affects code that compiles.
Nadri: The general property I wanted when starting this project was to make patterns more consistent. So if something needs to be mut, the compiler can just tell me that, and then I can add it to the pattern. So I don't like disallowing something for a corner case reason that would be difficult to explain to a non-expert. So for consistency, I'd like to allow this.
Nadri: `ref mut (mut x)` feel straightforward to me honestly. But we can also add it later if it comes up.
Josh: Agreed that removing the new syntax was the correct thing to do here, and I think it'd be a bad idea to attempt to introduce that syntax in this RFC; it *will* end up in bikeshedding. That said, I'm sympathetic to Niko's suggestion to prevent people from writing something that can't be fully expressed yet. You could always rebind (`let mut x = x;`) if you want a mutable binding.
NM: You could always rebind. But I also find Nadri's argument persuasive.
NM: I feel like this RFC puts us into an inconsistent position that then we have to resolve with a future bikeshed on the new syntax. So we can leave ourselves there OR we can make it an error now and leave room to add Nadri's consistent syntax later. I do wonder if we should just do the syntax up front.
pnkfelix: Would this proposal make it an error to have an inherited reference, or just mutable ones?
Nadri: It'd be all.
pnkfelix: That may be more common than we'd expect then.
Nadri: If we do that, it wouldn't break any code that exists today.
```rust!
let [mut x] = &[A];
// in 2021: `mut x: A`
// to migrate to 2024, we turn it into:
let &[mut x] = &[A];
// regardless of the migration lint, a 2024-compatible version needs `mut x: A`, hence this does not trigger the "mut binding with inherited reference" error.
```
TC: If we add the error, it should be backward compatible within an edition to remove the error later, right?
NM: That does seem correct.
NM: Options that I see:
1. Make `mut x` in inherited patterns an error; convert `mut x` with inherited ref to `& (mut x)` or `&mut (mut x)` in Rust 2021.
* can be extended later backwards compatibly (even in Rust 2024) with the semantics described here (and an explicit syntax)
3. Support `mut x` with inherited refs but do not have an explicit syntax. We probably want an explicit syntax later, but we don't have to discuss it now.
4. Add `ref (mut x)` and `ref mut (mut x)` syntax (or some alternative) now
Desirable properties
1. You can change `x` to `mut x` to get mutability -- good for users! Writing `let mut x = x` is possible but surprising.
2. There is an explicit form in terms of `&` and `ref` etc for everything you can write with inherited mutability.
3. Property that, if you have a pattern P without inherited references, e.g., `let &(mut x) = &22` gives `mut x: i32`, and you delete an `&`, then the bindings below become of type `&T` (so e.g. `let mut x = &22`, now `mut x: &i32`)
* "weak version" == if both compile, it holds
TC: Setting this aside, do we have consensus on doing this otherwise? Then we can either choose 1 or commit to the bikeshed on 3?
Josh: Sounds like we do. We could do option 1, then we could later commit to the bikeshed on work out what we'd need to do for option 3 in later work.
tmandry: I think we do have consensus on everything else here, and I'm good to move forward.
tmandry: So to confirm, we'd be adding an error here for something that we do not support today.
Jules: That's correct.
tmandry: I like this proposal because it's internally consistent and future compatible. And also, I don't see that many use cases for making a mutable reference to a binding, and maybe if you need an extra line to do that it's OK. At least, that's clear.
pnkfelix: I'm aligned with doing this.
TC: Let's confirm the cases we'd error on here by example.
Nadri: Here's an example of that.
```rust
let [mut x] = &mut [22];
// in 2021: `mut x: i32` -- definitely surprising!
// in 2024: error because we can't write the desugared `&mut [ref mut (mut x)]`
let [mut x] = &[22];
// in 2021: `mut x: i32` -- definitely surprising!
// in 2024: error because we can't write the desugared `&[ref (mut x)]`
```
NM: I'm happy with option 1.
## Rationale of `mut` resetting binding mode
tmandry: Was this ever intentional, and what was the reasoning behind it? Is there a reason we would want this?
nikomatsakis: It was a decision we made. I'm trying to remember the rationale. I think it was partly because there is no way to desugar a `mut` (with inherited ref) binding to an existing `ref`. I don't see much discussion in the [original RFC](https://rust-lang.github.io/rfcs/2005-match-ergonomics.html). I kind of remember us discussing it and being like "whatever, just reset it", but it's possible my memory is faulty. In retrospect error seems like it might've been the right choice.
## Can we write...?
tmandry: Can you write this?
```rust!
let [&mut mut x] = &mut [22]; // mut x: i32
x = 44;
```
Jules: Yes.
pnkfelix: Would this still be allowed under option 2?
Jules: Yes. It's canceled out here. By the time that we get to the binding, there's no reference at that point.
tmandry: So the intuition here is that we're not giving an ergonomic way to get a mutable binding to a reference.
## Matching `&mut` behind `&`
tmandry: I was confused by the last section
```rust!
// No way to avoid the `ref`, even with this RFC
let &[&mut ref x] = &[&mut 42];
```
..can you not write..
```rust!
let &[x] = &[&mut 42]; // x: &mut i32
// ERROR: can't copy `&mut i32`
```
..or for that matter..
```rust!
let [&x] = &[&mut 42]; // x: &mut i32
// ERROR: can't copy `&mut i32`
```
Jules: No, you can't move out of a shared reference, borrow check yells at you
tmandry: I think that makes sense.
NM: OK, I think this is fine.
tmandry: What about
```rust!
let [&mut x] = &[&mut 42]; // x: &i32
```
(I'm realizing this is inconsistent.)
NM: It'd be nice if there were a way to get a ref to the contents, because in general an `&mut` under an `&` is effectively `&` anyway, but I can see that it's hard.
Jules: This was part of the original proposal.
NM: To what degree can we do this backward compatibly.
Jules: It would be backward compatible to do something later here.
NM: OK, that's good then. I think it's good to keep the RFC scoped to things that feel like "small extensions and cleanup" and not major surgery.
## Next steps
NM: Seems like overall we're aligned on this.
TC: Sounds like someone, NM perhaps, should propose FCP merge on the RFC modulo that we want to see it changed to reflect the consensus on Option 1.
NM: I'll proposed FCP merge here and file that as a concern.
TC: Jules, anything else you need out of us?
Jules: To confirm, everyone is happy with matching `&mut` with `&`; that had been discussed on the RFC.
tmandry: Yes, that makes sense to me.
Jules: Also, to confirm, we're happy with the "no `ref mut` binding under `&` pattern":
```rust
// Doesn't work with this rule...
let &[[&mut a]] = &[&mut [42]];
// `&mut` in pattern needs to match against either:
// - `&mut` in value at same position (there is none, so not possible)
// - inherited `&mut` (which the "never set default binding mode to `ref mut` behind `&`" rule
// downgrades to `&`)
// Instead write:
let &[[&a]] = &[&mut [42]];
// or
let &[&mut [a]] = &[&mut [42]];
```
Will now work with this rule:
```rust!
let &[[a]] = &[&mut [42]];
// a: &i32 (&mut 32 and move error without this rule, wouln need to use `ref`)
// Nadri: we could write `let [[a]] = ...` here, is there an example where the rule is really helpful?
```
```rust!
let [a] = &&mut [42];
// `a: &i32` - `&` takes precedence over `&mut` (in all editions)
```
Jules: This actually weakens the "local reasoning" aspect of the proposal, but I think it is worth it because
* If you have code that doesn't work you can replace an `&` with `&mut`, whereas with the alternative you would have to fall back to using `ref`
* This allows more things, e.g. the `let &[[a]] = &[&mut [42]];`
tmandry: Does the RFC need to decide this?
Jules: It's not a decision that can be delayed.
tmandry: Based on the qualitative arguments, I can definitely see why this may be desirable. I'll need to look back through this.
Jules: I'll update the RFC with some of these.
TC: Here's the FCP:
https://github.com/rust-lang/rfcs/pull/3627#issuecomment-2113121111