From cbf6f22fac4edf5875c47e4c40815e058a05c462 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 6 May 2024 16:44:40 -0400 Subject: [PATCH 01/26] Add match ergonomics 2024 RFC --- text/3627-match-ergonomics-2024.md | 337 +++++++++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 text/3627-match-ergonomics-2024.md diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md new file mode 100644 index 00000000000..c7cf7c7bf97 --- /dev/null +++ b/text/3627-match-ergonomics-2024.md @@ -0,0 +1,337 @@ +- 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 to by-value, which users do not expect; the + mutability of the binding seems like a separate concern from its type + (, + ) +- `&` 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 + (, + , + , + ) +- 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. + +# 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 + +// 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; + +//let &mut x = &&mut 3; // ERROR +``` + +## 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 appear to be 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 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. +- 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 currently +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. From f42a677d6d539be8b09be4a0ab598503ff150176 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 6 May 2024 17:29:58 -0400 Subject: [PATCH 02/26] Fix typo Co-authored-by: kennytm --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index c7cf7c7bf97..950be9ce996 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -59,7 +59,7 @@ 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: +// New in edition 2024: // `&` pattern consumes inherited `&` reference. let [&x] = &[42]; let _: u8 = x; From 6081324b2b27dfadeb67381491487afc0164dc71 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 6 May 2024 17:44:05 -0400 Subject: [PATCH 03/26] Fix another typo Co-authored-by: kennytm --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 950be9ce996..d0b29c15510 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -212,7 +212,7 @@ let [&a] = &[&0u8]; // `a` is `u8` on edition ≤ 2021, but `&u8` on edition ≥ let [mut a] = &[0u8]; // `a` is `u8` on edition ≤ 2021, but `&u8` on edition ≥ 2024 ``` -Instances of such incompatibilities appear to be common, but far from unknown +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 From 9e1bb8e5283d07d174f008c1f65b88ef6e90e6b6 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 6 May 2024 18:26:21 -0400 Subject: [PATCH 04/26] Expand motivation and examples --- text/3627-match-ergonomics-2024.md | 143 +++++++++++++++++++---------- 1 file changed, 93 insertions(+), 50 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index d0b29c15510..063d5019225 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -22,26 +22,63 @@ Various changes to the match ergonomics rules: 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 - (, - ) -- `&` 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 - (, - , - , - ) -- 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. +## `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 +(, +). + +```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 +(, +, +, +). + + +```rust +fn foo(arg: &(String, Vec, 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 @@ -49,7 +86,7 @@ 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: +//! All editions // `x` "inherits" the `&` from the scrutinee type. let [x] = &[42]; let _: &u8 = x; @@ -59,10 +96,21 @@ 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 2024: +//! Edition ≥ 2024 + // `&` pattern consumes inherited `&` reference. let [&x] = &[42]; let _: u8 = x; + +// Examples from motivation section + +fn foo(arg: &(String, Vec, u8)) { + let (s, v, &u) = arg; + let _: (&String, &Vec, u8) = (s, v, u); +} + +let [&x] = &[&42]; +let _: &u8 = x; ``` ## `&` matches against `&mut` @@ -71,40 +119,35 @@ In edition 2024 and above, `&` patterns can match against `&mut` references (including "inherited" references). ```rust +//! Edition ≥ 2024 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: +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation -```rust -// Old editions -let (x, mut y) = &(true, false); -let _: (&bool, bool) = (x, y); -``` +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). -This no longer happens on edition ≥ 2024. +## 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 (x, mut y) = &(true, false); -let _: (&bool, &bool) = (x, y); +//! Edition ≥ 2024 +let &[mut a] = &[42]; +a = &47; ``` -# 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 +//! Edition ≥ 2024 let &foo = &mut 42; let _: u8 = foo; ``` @@ -112,8 +155,9 @@ 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` +// ^~ERROR: replace `&` with `&mut ` let _: &mut u8 = foo; ``` @@ -125,6 +169,8 @@ 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; @@ -133,13 +179,18 @@ 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; @@ -150,9 +201,12 @@ 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 +``` -// Unchanged from old editions +```rust +//! All editions let &mut x = &mut 3; let _: u8 = x; @@ -166,24 +220,13 @@ let _: &&mut u8 = x; //let &mut x = &&mut 3; // ERROR ``` -## 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 +//! All editions (new) let &[[a]]; = &[&mut [42]]; let _: &u8 = a; @@ -193,7 +236,7 @@ let _: &u8 = a; ``` ```rust -// Edition ≥ 2024 +//! Edition ≥ 2024 let &[[&a]]; = &[&mut [42]]; let _: u8 = a; @@ -319,7 +362,7 @@ explicit `ref` is required. Notably, this can occur where `&mut` is nested behind `&`: ```rust -// No way to avoid the `ref` here currently +// No way to avoid the `ref`, even with this RFC let &[&mut ref x] = &[&mut 42]; ``` From 70650acac6e9ac12754c126c2279e1b60eb8b153 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 6 May 2024 18:39:40 -0400 Subject: [PATCH 05/26] Fix typos in examples --- text/3627-match-ergonomics-2024.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 063d5019225..9d5f73a88da 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -228,20 +228,20 @@ allows strictly more code to compile.) ```rust //! All editions (new) -let &[[a]]; = &[&mut [42]]; +let &[[a]] = &[&mut [42]]; let _: &u8 = a; -let &[[a]]; = &mut [&mut [42]]; +let &[[a]] = &mut [&mut [42]]; let _: &u8 = a; ``` ```rust //! Edition ≥ 2024 -let &[[&a]]; = &[&mut [42]]; +let &[[&a]] = &[&mut [42]]; let _: u8 = a; -//let &[[&mut a]]; = &[&mut [42]]; // ERROR +//let &[[&mut a]] = &[&mut [42]]; // ERROR ``` # Migration From 8e4b83cdab461249b370d540221f31e25c4ec2d7 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 6 May 2024 18:42:40 -0400 Subject: [PATCH 06/26] Add link in footnote --- text/3627-match-ergonomics-2024.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 9d5f73a88da..e16d057cdad 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -311,8 +311,8 @@ There are several motivations for allowing this: 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 `&`. +[^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 From 1d1d1f49094419b421670bfd33cbd73b419ff874 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 6 May 2024 20:40:06 -0400 Subject: [PATCH 07/26] Expand on "no `ref mut` behind `&`" --- text/3627-match-ergonomics-2024.md | 37 +++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index e16d057cdad..26aeb95aaf2 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -229,7 +229,7 @@ allows strictly more code to compile.) //! All editions (new) let &[[a]] = &[&mut [42]]; -let _: &u8 = a; +let _: &u8 = a; // previously `a` would be `&mut u8`, resulting in a move check error let &[[a]] = &mut [&mut [42]]; let _: &u8 = a; @@ -289,6 +289,34 @@ The proposed rules for new editions uphold the following property: 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 `&` + +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. + +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]]; +``` + +Therefore, we *cannot* delay a decision on this matter. + ## `&` patterns matching against `&mut` There are several motivations for allowing this: @@ -318,13 +346,6 @@ that prevents the default binding mode from being set to `ref mut` behind `&`. 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 From e222363d4b9935dd2cc5359a661117198fe8d590 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 6 May 2024 20:44:02 -0400 Subject: [PATCH 08/26] Add comment to new example --- text/3627-match-ergonomics-2024.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 26aeb95aaf2..7606409fa75 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -313,6 +313,10 @@ 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. From 9b44733384605e158802ecad9e880243b93aacf1 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 7 May 2024 15:38:42 -0400 Subject: [PATCH 09/26] Fix typo in example --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 7606409fa75..d633a46aa40 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -138,7 +138,7 @@ syntax for this is left to a future RFC.) ```rust //! Edition ≥ 2024 -let &[mut a] = &[42]; +let [mut a] = &[42]; a = &47; ``` From 0e1306bf7eb767bfbf9c3bfc132ee1531afd6363 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 7 May 2024 23:14:46 -0400 Subject: [PATCH 10/26] Expand "no `ref mut` behind `&`" motivation --- text/3627-match-ergonomics-2024.md | 47 +++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index d633a46aa40..405c561faac 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -298,13 +298,7 @@ anyway is that the current behavior is unintuitive and surprising for users. ## Never setting default binding mode to `ref mut` behind `&` -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. +### 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 @@ -321,6 +315,45 @@ let &[[&mut a]] = &[&mut [42]]; 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: From e649309b196b934a84a942ed21e6f9ba9ab61b5e Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 8 May 2024 21:26:07 -0400 Subject: [PATCH 11/26] Expand on deref patterns --- text/3627-match-ergonomics-2024.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 405c561faac..6798111eee0 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -393,7 +393,8 @@ 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. +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.) @@ -409,10 +410,17 @@ mutabilities should be considered compatible. - An explicit syntax for mutable by-reference bindings should 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. +## 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 From a75166ce94e4f072a94b23dc37b4c063c02e264b Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 11 May 2024 00:21:43 -0400 Subject: [PATCH 12/26] Update "desirable property" --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 6798111eee0..b38e02e25f5 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -278,7 +278,7 @@ over an edition. 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 +> 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 From ab8be9af227bc8eac4fa262506ae00dae656d0ef Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 11 May 2024 00:23:25 -0400 Subject: [PATCH 13/26] Remove emphasis --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index b38e02e25f5..b88678fb08c 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -313,7 +313,7 @@ let &[[&mut a]] = &[&mut [42]]; // downgrades to `&`) ``` -Therefore, we *cannot* delay a decision on this matter. +Therefore, we cannot delay a decision on this matter. ### Makes behavior more consistent From 5754a9cd32bbedeb278a218f811a46896cfbb1af Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 12 May 2024 13:05:41 -0400 Subject: [PATCH 14/26] Migration lint macro troubles --- text/3627-match-ergonomics-2024.md | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index b88678fb08c..6b5b0b80689 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -263,6 +263,38 @@ 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 From 19e0c7cf446536253009d939da16acd21a59a5f0 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 13 May 2024 23:43:47 -0400 Subject: [PATCH 15/26] Fix example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arrays are homogeneous… --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 6b5b0b80689..e4b40bcce1d 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -284,7 +284,7 @@ extern crate foo; use foo::*; fn main() { - let [[&x], foo!(y)] = &[[&0], [0]]; + let ([&x], foo!(y)) = &([&0], [0]); //~^ WARN: the semantics of this pattern will change in edition 2024 let _: i32 = x; let _: &i32 = y; From 099b71f0ea34c2aec8e30826f9b9185bc361dbd7 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 15 May 2024 14:21:56 -0400 Subject: [PATCH 16/26] No more implicit `mut ref mut` --- text/3627-match-ergonomics-2024.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index e4b40bcce1d..772f794e7cc 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -72,11 +72,11 @@ 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. +Instead, `mut` on a binding with non-default binding mode is an error. ```rust //! Edition ≥ 2024 -let (x, mut y) = &(true, false); -let _: (&bool, &bool) = (x, y); // instead of `(&bool, bool)` +//let (x, mut y) = &(true, false); // ERROR ``` ## Matching against inherited references @@ -132,14 +132,12 @@ including the "default binding mode" terminology. Refer to [RFC 2005](./2005-mat ## 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.) +In the new edition, `mut` no longer resets the binding mode to by-value; +instead, `mut` on a binding with a by-reference binding mode is an error. ```rust //! Edition ≥ 2024 -let [mut a] = &[42]; -a = &47; +// let [mut a] = &[42]; //ERROR ``` ## Edition 2024: `&` patterns can match against `&mut` references @@ -441,7 +439,8 @@ concerns with certain proposals for "deref patterns". [future-possibilities]: #future-possibilities - An explicit syntax for mutable by-reference bindings should be chosen at some - point. + point, along with removing the prohibition on implicitly by-reference + mutable bindings. - Future changes to reference types (partial borrows, language sugar for `Pin`, etc) may interact with match ergonomics. @@ -461,7 +460,7 @@ behind `&`: ```rust // No way to avoid the `ref`, even with this RFC -let &[&mut ref x] = &[&mut 42]; +let &[&mut ref x] = &[&mut 42]; // x: &i32 ``` There are two strategies we could take to support this: From 07b35d840a539e69163c5c791101197cdbaed2d7 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 15 May 2024 14:32:18 -0400 Subject: [PATCH 17/26] Add more examples to "no `ref mut` behind `&`" rationale --- text/3627-match-ergonomics-2024.md | 55 +++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 772f794e7cc..ba744604109 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -330,30 +330,59 @@ anyway is that the current behavior is unintuitive and surprising for users. ### 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: +#### Patterns that work only with this rule ```rust -let &[[&mut a]] = &[&mut [42]]; +//! All editions: works only with this rule +let &[[a]] = &[&mut [42]]; // x: &i32 +``` + +```rust +//! All editions: works with or without this rule (alternative to above) +// Note the explicit `ref`, we must abandon match ergonomics +let &[[ref a]] = &[&mut [42]]; // x: &i32 +let &[&mut [ref a]] = &[&mut [42]]; // x: &i32 +``` + +#### Patterns that work only without this rule + +```rust +//! Edition ≥ 2024: works only without this rule +let &[[&mut a]] = &[&mut [42]]; // x: i32 // `&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 `&`) +// - inherited `&mut` (which the rule downgrades to `&`) ``` -Therefore, we cannot delay a decision on this matter. +```rust +//! Edition ≥ 2024: works with or without this rule (alternatives to above) +// No need to abandon match ergonomics +let &[[&a]] = &[&mut [42]]; // x: i32 +let &[&mut [&a]] = &[&mut [42]]; // x: i32 +``` ### 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`. +default binding mode is already `ref mut`, the binding mode gets set to `ref`: + +```rust +//! All editions +let [a] = &mut &[42]; // x: &i32 +``` + 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. +off, the binding mode remains `ref`: + +```rust +//! All editions +let [a] = &&mut [42]; // x: &i32 +``` + +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 From 3ff02677615de16b3a92e0d5d07615d360f4f5f6 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 16 May 2024 15:34:39 -0400 Subject: [PATCH 18/26] Fix typo Co-authored-by: Waffle Maybe --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index ba744604109..c486358d02a 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -289,7 +289,7 @@ fn main() { } ``` -In such cases, there is no possible machine-applicable suggstion we could emit +In such cases, there is no possible machine-applicable suggestion we could emit to produce code compatible with all editions (short of expanding the macro). However, such code should be extremely rare in practice. From b07848501c2bc7db00f322f4e85a7a814ed79e62 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 16 May 2024 20:41:27 -0400 Subject: [PATCH 19/26] Fix typo Co-authored-by: Tyler Mandry --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index c486358d02a..191dd7764b9 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -358,7 +358,7 @@ let &[[&mut a]] = &[&mut [42]]; // x: i32 //! Edition ≥ 2024: works with or without this rule (alternatives to above) // No need to abandon match ergonomics let &[[&a]] = &[&mut [42]]; // x: i32 -let &[&mut [&a]] = &[&mut [42]]; // x: i32 +let &[&mut [a]] = &[&mut [42]]; // x: i32 ``` ### Makes behavior more consistent From c2bdf38088fecdbe261d007c3cf1a567eec21e37 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 16 May 2024 23:14:22 -0400 Subject: [PATCH 20/26] Fairer examples --- text/3627-match-ergonomics-2024.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 191dd7764b9..ab8b9a6d5f9 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -334,14 +334,14 @@ anyway is that the current behavior is unintuitive and surprising for users. ```rust //! All editions: works only with this rule -let &[[a]] = &[&mut [42]]; // x: &i32 +let &(i, j, [s]) = &(63, 42, &mut [String::from("🦀")]); // i: i32, j: i32, s: &String ``` ```rust //! All editions: works with or without this rule (alternative to above) -// Note the explicit `ref`, we must abandon match ergonomics -let &[[ref a]] = &[&mut [42]]; // x: &i32 -let &[&mut [ref a]] = &[&mut [42]]; // x: &i32 +let (&i, &j, [s]) = &(42, &mut [String::from("🦀")]); // i: i32, j: i32, s: &String +let &(i, j, [ref s]) = &(42, &mut [String::from("🦀")]); // i: i32, j: i32, s: &String +let &(i, j, &mut [ref s]) = &(42, &mut [String::from("🦀")]); // i: i32, j: i32, s: &String ``` #### Patterns that work only without this rule @@ -356,7 +356,6 @@ let &[[&mut a]] = &[&mut [42]]; // x: i32 ```rust //! Edition ≥ 2024: works with or without this rule (alternatives to above) -// No need to abandon match ergonomics let &[[&a]] = &[&mut [42]]; // x: i32 let &[&mut [a]] = &[&mut [42]]; // x: i32 ``` @@ -468,8 +467,8 @@ concerns with certain proposals for "deref patterns". [future-possibilities]: #future-possibilities - An explicit syntax for mutable by-reference bindings should be chosen at some - point, along with removing the prohibition on implicitly by-reference - mutable bindings. + point, along with removing the prohibition on implicitly by-reference mutable + bindings. - Future changes to reference types (partial borrows, language sugar for `Pin`, etc) may interact with match ergonomics. @@ -484,8 +483,7 @@ question that would need to be resolved is whether and how deref patterns ## 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 `&`: +explicit `ref` is required. This happens where `&mut` is nested behind `&`: ```rust // No way to avoid the `ref`, even with this RFC From 04501bc53af94a6d65e49bd1098d704cf834c532 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 16 May 2024 23:19:22 -0400 Subject: [PATCH 21/26] Fix fairer example --- text/3627-match-ergonomics-2024.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index ab8b9a6d5f9..3271da2feac 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -338,8 +338,12 @@ let &(i, j, [s]) = &(63, 42, &mut [String::from("🦀")]); // i: i32, j: i32, s: ``` ```rust -//! All editions: works with or without this rule (alternative to above) +//! Edition ≥ 2024: works with or without this rule (alternative to above) let (&i, &j, [s]) = &(42, &mut [String::from("🦀")]); // i: i32, j: i32, s: &String +``` + +```rust +//! All editions: works with or without this rule (alternatives to above) let &(i, j, [ref s]) = &(42, &mut [String::from("🦀")]); // i: i32, j: i32, s: &String let &(i, j, &mut [ref s]) = &(42, &mut [String::from("🦀")]); // i: i32, j: i32, s: &String ``` From d623f9c2a57cd61353696143f529451d7399a53d Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 3 Jul 2024 10:42:02 -0400 Subject: [PATCH 22/26] `&` matches `&mut` on all editions --- text/3627-match-ergonomics-2024.md | 34 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 3271da2feac..9311fc8fdc8 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -12,7 +12,7 @@ Various changes to the match ergonomics rules: 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, `&` 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. @@ -34,11 +34,11 @@ let (x, mut y) = &(true, false); let _: (&bool, bool) = (x, y); ``` -## Can't cancel out an inherited reference +## 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 +have no general mechanism to “cancel out” an inherited reference (, , , @@ -71,8 +71,8 @@ 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. -Instead, `mut` on a binding with non-default binding mode is an error. +`mut` on a binding does not reset the binding mode on edition ≥ 2024. Instead, +`mut` on a binding with non-default binding mode is an error. ```rust //! Edition ≥ 2024 @@ -115,15 +115,21 @@ let _: &u8 = x; ## `&` matches against `&mut` -In edition 2024 and above, `&` patterns can match against `&mut` references -(including "inherited" references). +On all editions, `&` patterns can match against `&mut` references (on edition +2024 and above, this includes "inherited" references). ```rust -//! Edition ≥ 2024 +//! All editions let &foo = &mut 42; let _: u8 = foo; ``` +```rust +//! Edition ≥ 2024 +let [&foo] = &mut [42]; +let _: u8 = foo; +``` + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -140,12 +146,12 @@ instead, `mut` on a binding with a by-reference binding mode is an error. // let [mut a] = &[42]; //ERROR ``` -## Edition 2024: `&` patterns can match against `&mut` references +## All editions: `&` patterns can match against `&mut` references `&` patterns can match against `&mut` references. ```rust -//! Edition ≥ 2024 +//! All editions let &foo = &mut 42; let _: u8 = foo; ``` @@ -328,7 +334,7 @@ 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 +### We can’t delay this choice #### Patterns that work only with this rule @@ -423,14 +429,14 @@ 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 + 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 +- 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. @@ -478,7 +484,7 @@ concerns with certain proposals for "deref patterns". ## Deref patterns -Because it is compositional, the "eat-one-layer" model proposed by this RFC is +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 From 27717e6fd082ea52403c6c1d5dcdb2f66830e29b Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 3 Jul 2024 10:53:47 -0400 Subject: [PATCH 23/26] Incorporate TC's additions --- text/3627-match-ergonomics-2024.md | 61 ++++++++++++++++-------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 9311fc8fdc8..0e594dfec20 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -79,6 +79,23 @@ Match ergonomics works a little differently in edition 2024 and above. //let (x, mut y) = &(true, false); // ERROR ``` +## `&` matches against `&mut` + +On all editions, `&` patterns can match against `&mut` references. On edition +2024 and above, this includes "inherited" references as described below. + +```rust +//! All editions +let &foo = &mut 42; +let _: u8 = foo; +``` + +```rust +//! Edition ≥ 2024 +let [&foo] = &mut [42]; +let _: u8 = foo; +``` + ## Matching against inherited references In all editions, when you match against an `&` or `&mut` reference with the type @@ -113,23 +130,6 @@ let [&x] = &[&42]; let _: &u8 = x; ``` -## `&` matches against `&mut` - -On all editions, `&` patterns can match against `&mut` references (on edition -2024 and above, this includes "inherited" references). - -```rust -//! All editions -let &foo = &mut 42; -let _: u8 = foo; -``` - -```rust -//! Edition ≥ 2024 -let [&foo] = &mut [42]; -let _: u8 = foo; -``` - # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -201,14 +201,18 @@ 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. +If the default binding mode is `ref`, then `&mut` patterns will not be able to +match against it, so they will match structurally instead (preserving the +binding mode). ```rust //! Edition ≥ 2024 -//let [&mut x] = &[&mut 42]; // ERROR +let [&mut x] = &[&mut 42]; +let _: &u8 = x; ``` +`&mut` patterns are otherwise unchanged. + ```rust //! All editions @@ -490,25 +494,26 @@ fully compatible with proposals for "deref patterns", including allowing question that would need to be resolved is whether and how deref patterns (explicit or implicit) affect the default binding mode. -## Matching `&mut` behind `&` +## Matching `&mut` directly behind `&` There is one notable situation where match ergonomics cannot be used, and explicit `ref` is required. This happens where `&mut` is nested behind `&`: ```rust // No way to avoid the `ref`, even with this RFC -let &[&mut ref x] = &[&mut 42]; // x: &i32 +let &&mut ref x = &&mut 42; // x: &i32 ``` 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`. +- `&mut` patterns could “strip off” outer `&`. 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. + for example) as it potentially 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];`. + `&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. From 97bf386e8e04e8267b706460602fb08046286f87 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 4 Jul 2024 10:59:53 -0400 Subject: [PATCH 24/26] `&` constrains inference --- text/3627-match-ergonomics-2024.md | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index 0e594dfec20..a56eec5cb3a 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -165,6 +165,58 @@ let &ref mut foo = &mut 42; let _: &mut u8 = foo; ``` +However, if the type of the scrutinee is unknown, an `&` pattern will still +constrain inference to force it to be a shared reference. + +```rust +//! All editions +fn generic() -> (R, bool) { + R::meow() +} + +trait Ref: Sized { + fn meow() -> (Self, bool); +} + +impl Ref for &'static [(); 0] { + fn meow() -> (Self, bool) { + (&[], false) + } +} + +impl Ref for &'static mut [(); 0] { + fn meow() -> (Self, bool) { + (&mut [], true) + } +} + +fn main() { + let (&_, b) = generic(); + assert!(!b); +} +``` + +```rust +//! All editions +fn generic() -> R { + R::meow() +} + +trait Ref: Sized { + fn meow() -> Self; +} + +impl Ref for &'static mut [(); 0] { + fn meow() -> Self { + &mut [] + } +} + +fn main() { + let &_ = generic(); //~ERROR[E0277]: the trait bound `&_: Ref` is not satisfied +} +``` + ## Edition 2024: `&` and `&mut` can match against inherited references When the default binding mode is `ref` or `ref mut`, `&` and `&mut` patterns can From 88267c9839d156d0aed5e4a98bc77f51fd3e302a Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Thu, 4 Jul 2024 20:16:31 +0000 Subject: [PATCH 25/26] Add the numbered match ergonomics rules adopted by lang In the lang design meeting on 2024-06-26, we adopted a slate of five rules for match ergonomics. We later amended *Rule 4* in our meeting on 2024-07-03. These five rules, as amended, form the normative description of what the lang team means for the rules to be. Therefore, in the reference-level explanation, let's include these rules verbatim and note that the remaining sections describe these rules further. --- text/3627-match-ergonomics-2024.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index a56eec5cb3a..d50f7d538ef 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -136,6 +136,33 @@ let _: &u8 = x; 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). +## The rules in brief + +Building on the rules of [RFC 2005](./2005-match-ergonomics.md), this RFC adopts +the following five rules for match ergonomics: + +- **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 `&` 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. +- **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). + +*Rule 1* and *Rule 2* are edition-dependent and will be stabilized with +Rust 2024. The other three rules will be stabilized in all editions. + +In the sections below, we describe these rules and their effects in more detail. + ## 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; From 28b91fb2b450c8f00dd2ac10a5eab6fed874dac7 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sun, 14 Jul 2024 06:27:11 +0000 Subject: [PATCH 26/26] Prepare RFC 3627 to be merged --- text/3627-match-ergonomics-2024.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3627-match-ergonomics-2024.md b/text/3627-match-ergonomics-2024.md index d50f7d538ef..84944d785c9 100644 --- a/text/3627-match-ergonomics-2024.md +++ b/text/3627-match-ergonomics-2024.md @@ -1,7 +1,7 @@ - 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) +- Tracking Issue: [rust-lang/rust#123076](https://github.com/rust-lang/rust/issues/123076) # Summary [summary]: #summary