From a199394caebb7f5416cb24b9a576124de28f32a5 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Mon, 16 Oct 2017 08:05:52 +0200 Subject: [PATCH 1/7] rfc, if_while_or_patterns: initial version --- text/0000-if-while-or-patterns.md | 244 ++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 text/0000-if-while-or-patterns.md diff --git a/text/0000-if-while-or-patterns.md b/text/0000-if-while-or-patterns.md new file mode 100644 index 00000000000..d36cdf99b03 --- /dev/null +++ b/text/0000-if-while-or-patterns.md @@ -0,0 +1,244 @@ +- Feature Name: if_while_or_patterns +- Start Date: 2017-10-16 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Enables "or" patterns for [`if let` and `while let` expressions](https://github.com/rust-lang/rfcs/pull/160). In other words, examples like the following are now possible: + +```rust +enum E { + A(T), B(T), C, D, E, F +} + +// Assume the enum E and the following for the remainder of the RFC: +use E::*; + +let x = A(1); +let r = if let C | D = x { 1 } else { 2 }; + +while let A(x) | B(x) = source() { + react_to(x); +} +``` + +# Motivation +[motivation]: #motivation + +While nothing in this RFC is currently impossible in Rust, the changes the RFC proposes improves the ergonomics of control flow when dealing with `enum`s (sum types) with three or more variants where the program should react in one way to a group of variants, and another way to another group of variants. Examples of when such sum types occur are protocols and when dealing with languages (ASTs). + +The following snippet (written with this RFC): + +```rust +if let A(x) | B(x) = expr { + do_stuff_with(x); +} +``` + +must be written as: + +```rust +if let A(x) = expr { + do_stuff_with(x); +} else if let B(x) = expr { + do_stuff_with(x); +} +``` + +or, using `match`: + +```rust +match expr { + A(x) | B(x) => do_stuff_with(x), + _ => {}, +} +``` + +With `while let`, the ergonomics and in particular the readability can be significantly improved. + +The following snippet (written with this RFC): + +```rust +while let A(x) | B(x) = source() { + react_to(x); +} +``` + +must currently be written as: + +```rust +loop { + match source() { + A(x) | B(x) => react_to(x), + _ => { break; } + } +} +``` + +Another major motivation of the RFC is concistency with `match`. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +[RFC 2005](https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#examples), in describing the third example in the section "Examples", refers to patterns with `|` in them as "or" patterns. This RFC adopts the same terminology. + +While the "sum" of all patterns in `match` must be irrefutable, or in other words: cover all cases, be exhaustive, this is not the case (currently) with `if/while let`, which may have a refutable pattern. This RFC does not change this. + +The RFC only extends the use of or-patterns from `match`es to `if let` and `while let` expressions. + +For examples, see [motivation]. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## Grammar + +### `if let` + +The grammar in [§ 7.2.24](https://doc.rust-lang.org/grammar.html#if-let-expressions) is changed from: + +``` +if_let_expr : "if" "let" pat '=' expr '{' block '}' + else_tail ? ; +``` + +to: + +``` +if_let_expr : "if" "let" pat [ '|' pat ] * '=' expr '{' block '}' + else_tail ? ; +``` + +### `while let` + +The grammar in [§ 7.2.25](https://doc.rust-lang.org/grammar.html#while-let-loops) is changed from: + +``` +while_let_expr : [ lifetime ':' ] ? "while" "let" pat '=' expr '{' block '}' ; +``` + +to: + +``` +while_let_expr : [ lifetime ':' ] ? "while" "let" pat [ '|' pat ] * '=' expr '{' block '}' ; +``` + +## Syntax lowering + +The changes proposed in this RFC can be implemented by transforming the `if/while let` constructs with a syntax-lowering pass into `match` and `loop` + `match` expressions. + +### Examples, `if let` + +These examples are extensions on the [`if let` RFC](https://github.com/rust-lang/rfcs/pull/160). Therefore, the RFC avoids +duplicating any details already specified there. + +Source: +```rust +if let PAT [| PAT]* = EXPR { BODY } +``` +Result: +```rust +match EXPR { + PAT [| PAT]* => { BODY } + _ => {} +} +``` + +Source: +```rust +if let PAT [| PAT]* = EXPR { BODY_IF } else { BODY_ELSE } +``` +Result: +```rust +match EXPR { + PAT [| PAT]* => { BODY_IF } + _ => { BODY_ELSE } +} +``` + +Source: +```rust +if COND { + BODY_IF +} else if let PAT [| PAT]* = EXPR { + BODY_ELSE_IF +} else { + BODY_ELSE +} +``` +Result: +```rust +if COND { + BODY_IF +} else { + match EXPR { + PAT [| PAT]* => { BODY_ELSE_IF } + _ => { BODY_ELSE } + } +} +``` + +Source +```rust +if let PAT [| PAT]* = EXPR { + BODY_IF +} else if COND { + BODY_ELSE_IF_1 +} else if OTHER_COND { + BODY_ELSE_IF_2 +} +``` +Result: +```rust +match EXPR { + PAT [| PAT]* => { BODY_IF } + _ if COND => { BODY_ELSE_IF_1 } + _ if OTHER_COND => { BODY_ELSE_IF_2 } + _ => {} +} +``` + +### Examples, `while let` + +The following example is an extension on the [`while let` RFC](https://github.com/rust-lang/rfcs/pull/214). + +Source +```rust +['label:] while let PAT [| PAT]* = EXPR { + BODY +} +``` +Result: +```rust +['label:] loop { + match EXPR { + PAT [| PAT]* => BODY, + _ => break + } +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +It's one more addition to the grammar. + +# Rationale and alternatives +[alternatives]: #alternatives + +This could simply not be done. +Consistency with `match` is however on its own reason enough to do this. + +It could be claimed that the `if/while let` RFCs already mandates this RFC, +this RFC does answer that question and instead simply mandates it now. + +# Unresolved questions +[unresolved]: #unresolved-questions + +The exact syntax transformations should be deferred to the implementation. +This RFC does not mandate exactly how the AST:s should be transformed, only +that the feature be supported. + +There are no unresolved questions. \ No newline at end of file From e14747378a51a7456e5e9bd20f9c7b3d002f3727 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Mon, 16 Oct 2017 08:11:13 +0200 Subject: [PATCH 2/7] rfc, if_while_or_patterns: fixed links --- text/0000-if-while-or-patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-if-while-or-patterns.md b/text/0000-if-while-or-patterns.md index d36cdf99b03..4bf776782b7 100644 --- a/text/0000-if-while-or-patterns.md +++ b/text/0000-if-while-or-patterns.md @@ -6,7 +6,7 @@ # Summary [summary]: #summary -Enables "or" patterns for [`if let` and `while let` expressions](https://github.com/rust-lang/rfcs/pull/160). In other words, examples like the following are now possible: +Enables "or" patterns for [`if let`](https://github.com/rust-lang/rfcs/pull/160) and [`while let`](https://github.com/rust-lang/rfcs/pull/214) expressions. In other words, examples like the following are now possible: ```rust enum E { From f9179b82b87cf7cc62752e1565be3cc5dcf14fed Mon Sep 17 00:00:00 2001 From: Mazdak Date: Mon, 16 Oct 2017 08:20:56 +0200 Subject: [PATCH 3/7] rfc, if_while_or_patterns: fixed typos + other linguistic improvements --- text/0000-if-while-or-patterns.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-if-while-or-patterns.md b/text/0000-if-while-or-patterns.md index 4bf776782b7..f3d093cc36a 100644 --- a/text/0000-if-while-or-patterns.md +++ b/text/0000-if-while-or-patterns.md @@ -77,7 +77,7 @@ loop { } ``` -Another major motivation of the RFC is concistency with `match`. +Another major motivation of the RFC is consistency with `match`. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -231,7 +231,7 @@ It's one more addition to the grammar. This could simply not be done. Consistency with `match` is however on its own reason enough to do this. -It could be claimed that the `if/while let` RFCs already mandates this RFC, +It could be claimed that the `if/while let` RFCs already mandate this RFC, this RFC does answer that question and instead simply mandates it now. # Unresolved questions @@ -239,6 +239,6 @@ this RFC does answer that question and instead simply mandates it now. The exact syntax transformations should be deferred to the implementation. This RFC does not mandate exactly how the AST:s should be transformed, only -that the feature be supported. +that the or-pattern feature be supported. There are no unresolved questions. \ No newline at end of file From 7739d40404f32dcd90c2dd4ea21e4199589f68c9 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 15:05:16 +0200 Subject: [PATCH 4/7] rfc, if_while_or_patterns: expanded motivation + fixed formatting --- text/0000-if-while-or-patterns.md | 91 +++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/text/0000-if-while-or-patterns.md b/text/0000-if-while-or-patterns.md index f3d093cc36a..00d0cdfc1fc 100644 --- a/text/0000-if-while-or-patterns.md +++ b/text/0000-if-while-or-patterns.md @@ -6,7 +6,11 @@ # Summary [summary]: #summary -Enables "or" patterns for [`if let`](https://github.com/rust-lang/rfcs/pull/160) and [`while let`](https://github.com/rust-lang/rfcs/pull/214) expressions. In other words, examples like the following are now possible: +[`if let`]: https://github.com/rust-lang/rfcs/pull/160 +[`while let`]: https://github.com/rust-lang/rfcs/pull/214 + +Enables "or" patterns for [`if let`] and [`while let`] expressions. In other +words, examples like the following are now possible: ```rust enum E { @@ -27,7 +31,12 @@ while let A(x) | B(x) = source() { # Motivation [motivation]: #motivation -While nothing in this RFC is currently impossible in Rust, the changes the RFC proposes improves the ergonomics of control flow when dealing with `enum`s (sum types) with three or more variants where the program should react in one way to a group of variants, and another way to another group of variants. Examples of when such sum types occur are protocols and when dealing with languages (ASTs). +While nothing in this RFC is currently impossible in Rust, the changes the RFC +proposes improves the ergonomics of control flow when dealing with `enum`s +(sum types) with three or more variants where the program should react in one +way to a group of variants, and another way to another group of variants. +Examples of when such sum types occur are protocols, when dealing with +languages (ASTs), and non-trivial iterators. The following snippet (written with this RFC): @@ -56,7 +65,51 @@ match expr { } ``` -With `while let`, the ergonomics and in particular the readability can be significantly improved. +[`std::iter`]: https://doc.rust-lang.org/nightly/src/core/iter/mod.rs.html#691 + +This way of using `match` is seen multiple times in [`std::iter`] when dealing +with the `Chain` iterator adapter. An example of this is: + +```rust + fn fold(self, init: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc, + { + let mut accum = init; + match self.state { + ChainState::Both | ChainState::Front => { + accum = self.a.fold(accum, &mut f); + } + _ => { } + } + match self.state { + ChainState::Both | ChainState::Back => { + accum = self.b.fold(accum, &mut f); + } + _ => { } + } + accum + } +``` + +which could have been written as: + +```rust + fn fold(self, init: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc, + { + use ChainState::*; + let mut accum = init; + if let Both | Front = self.state { accum = self.a.fold(accum, &mut f); } + if let Both | Back = self.state { accum = self.b.fold(accum, &mut f); } + accum + } +``` + +This version is both shorter and clearer. + + +With `while let`, the ergonomics and in particular the readability can be +significantly improved. The following snippet (written with this RFC): @@ -82,11 +135,18 @@ Another major motivation of the RFC is consistency with `match`. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -[RFC 2005](https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#examples), in describing the third example in the section "Examples", refers to patterns with `|` in them as "or" patterns. This RFC adopts the same terminology. +[RFC 2005]: https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#examples -While the "sum" of all patterns in `match` must be irrefutable, or in other words: cover all cases, be exhaustive, this is not the case (currently) with `if/while let`, which may have a refutable pattern. This RFC does not change this. +[RFC 2005], in describing the third example in the section "Examples", refers to +patterns with `|` in them as "or" patterns. This RFC adopts the same terminology. -The RFC only extends the use of or-patterns from `match`es to `if let` and `while let` expressions. +While the "sum" of all patterns in `match` must be irrefutable, or in other +words: cover all cases, be exhaustive, this is not the case (currently) with +`if/while let`, which may have a refutable pattern. +This RFC does not change this. + +The RFC only extends the use of or-patterns from `match`es to `if let` and +`while let` expressions. For examples, see [motivation]. @@ -95,9 +155,12 @@ For examples, see [motivation]. ## Grammar +[§ 7.2.24]: https://doc.rust-lang.org/grammar.html#if-let-expressions +[§ 7.2.25]: https://doc.rust-lang.org/grammar.html#while-let-loops + ### `if let` -The grammar in [§ 7.2.24](https://doc.rust-lang.org/grammar.html#if-let-expressions) is changed from: +The grammar in [§ 7.2.24] is changed from: ``` if_let_expr : "if" "let" pat '=' expr '{' block '}' @@ -113,7 +176,7 @@ if_let_expr : "if" "let" pat [ '|' pat ] * '=' expr '{' block '}' ### `while let` -The grammar in [§ 7.2.25](https://doc.rust-lang.org/grammar.html#while-let-loops) is changed from: +The grammar in [§ 7.2.25] is changed from: ``` while_let_expr : [ lifetime ':' ] ? "while" "let" pat '=' expr '{' block '}' ; @@ -127,11 +190,15 @@ while_let_expr : [ lifetime ':' ] ? "while" "let" pat [ '|' pat ] * '=' expr '{' ## Syntax lowering -The changes proposed in this RFC can be implemented by transforming the `if/while let` constructs with a syntax-lowering pass into `match` and `loop` + `match` expressions. +The changes proposed in this RFC can be implemented by transforming the +`if/while let` constructs with a syntax-lowering pass into `match` and +`loop` + `match` expressions. ### Examples, `if let` -These examples are extensions on the [`if let` RFC](https://github.com/rust-lang/rfcs/pull/160). Therefore, the RFC avoids +[`if let` RFC]: https://github.com/rust-lang/rfcs/pull/160 + +These examples are extensions on the [`if let` RFC]. Therefore, the RFC avoids duplicating any details already specified there. Source: @@ -202,7 +269,9 @@ match EXPR { ### Examples, `while let` -The following example is an extension on the [`while let` RFC](https://github.com/rust-lang/rfcs/pull/214). +[`while let` RFC]: https://github.com/rust-lang/rfcs/pull/214 + +The following example is an extension on the [`while let` RFC]. Source ```rust From 85f8cb04bfea7cd0b1a12cec3a5fb59244ce5512 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 29 Jan 2018 17:47:03 +0100 Subject: [PATCH 5/7] rfc, if_while_or_patterns: amended with support for | in let statements --- text/0000-if-while-or-patterns.md | 77 +++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/text/0000-if-while-or-patterns.md b/text/0000-if-while-or-patterns.md index 00d0cdfc1fc..da52d3ffe69 100644 --- a/text/0000-if-while-or-patterns.md +++ b/text/0000-if-while-or-patterns.md @@ -9,8 +9,8 @@ [`if let`]: https://github.com/rust-lang/rfcs/pull/160 [`while let`]: https://github.com/rust-lang/rfcs/pull/214 -Enables "or" patterns for [`if let`] and [`while let`] expressions. In other -words, examples like the following are now possible: +Enables "or" patterns for [`if let`] and [`while let`] expressions as well as +`let` statements. In other words, examples like the following are now possible: ```rust enum E { @@ -26,6 +26,11 @@ let r = if let C | D = x { 1 } else { 2 }; while let A(x) | B(x) = source() { react_to(x); } + +enum ParameterKind { Ty(T), Lifetime(L), } + +// Only possible when `L = T` such that `kind : ParameterKind`. +let Ty(x) | Lifetime(x) = kind; ``` # Motivation @@ -107,7 +112,6 @@ which could have been written as: This version is both shorter and clearer. - With `while let`, the ergonomics and in particular the readability can be significantly improved. @@ -132,6 +136,10 @@ loop { Another major motivation of the RFC is consistency with `match`. +To keep `let` statements consistent with `if let`, and to enable the scenario +exemplified by `ParameterKind` in the [motivation], these or-patterns are +allowed at the top level of `let` statements. + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -145,8 +153,8 @@ words: cover all cases, be exhaustive, this is not the case (currently) with `if/while let`, which may have a refutable pattern. This RFC does not change this. -The RFC only extends the use of or-patterns from `match`es to `if let` and -`while let` expressions. +The RFC only extends the use of or-patterns at the top level from `match`es +to `if let` and `while let` expressions as well as `let` statements. For examples, see [motivation]. @@ -188,11 +196,28 @@ to: while_let_expr : [ lifetime ':' ] ? "while" "let" pat [ '|' pat ] * '=' expr '{' block '}' ; ``` +### `let` statements + +The statement `stmt` grammar is replaced with a language equivalent to: + +``` +stmt ::= old_stmt_grammar + | let_stmt_many + ; + +let_stmt_many ::= "let" pat_two_plus "=" expr ";" + +pat_two_plus ::= pat [ '|' pat ] + ; +``` + ## Syntax lowering -The changes proposed in this RFC can be implemented by transforming the -`if/while let` constructs with a syntax-lowering pass into `match` and -`loop` + `match` expressions. +The changes proposed in this RFC with respect to `if let` and `while let` +can be implemented by transforming the `if/while let` constructs with a +syntax-lowering pass into `match` and `loop` + `match` expressions. + +Meanwhile, `let` statements can be transformed into a continuation with +`match` as described below. ### Examples, `if let` @@ -289,10 +314,41 @@ Result: } ``` +## Desugaring `let` statements with `|` in the top-level pattern + +This is a possible desugaring that a Rust compiler may do. +While such a compiler may elect to implement this differently, +these semantics should be kept. + +Source: +```rust +{ + // prefix of statements: + stmt* + // The let statement which is the cause for desugaring: + let_stmt_many + // the continuation / suffix of statements: + stmt* + tail_expr? // Meta-variable for optional tail expression without ; at end +} +``` +Result +```rust +{ + stmt* + match expr { + pat_two_plus => { + stmt* + tail_expr? + } + } +} +``` + # Drawbacks [drawbacks]: #drawbacks -It's one more addition to the grammar. +This adds more additions to the grammar and makes the compiler more complex. # Rationale and alternatives [alternatives]: #alternatives @@ -303,6 +359,9 @@ Consistency with `match` is however on its own reason enough to do this. It could be claimed that the `if/while let` RFCs already mandate this RFC, this RFC does answer that question and instead simply mandates it now. +Another alternative is to only deal with `if/while let` expressions but not +`let` statements. + # Unresolved questions [unresolved]: #unresolved-questions From 21baf514845fa0be87c3c0ddc4082398eb44a394 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 30 Jan 2018 16:08:57 +0100 Subject: [PATCH 6/7] rfc, if_while_or_patterns: more motivation for or-patterns in let statements --- text/0000-if-while-or-patterns.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/text/0000-if-while-or-patterns.md b/text/0000-if-while-or-patterns.md index da52d3ffe69..d4f9ac87b17 100644 --- a/text/0000-if-while-or-patterns.md +++ b/text/0000-if-while-or-patterns.md @@ -140,6 +140,17 @@ To keep `let` statements consistent with `if let`, and to enable the scenario exemplified by `ParameterKind` in the [motivation], these or-patterns are allowed at the top level of `let` statements. +In addition to the `ParameterKind` example, we can also consider +`slice.binary_search(&x)`. If we are only interested in the `index` at where +`x` is or would be, without any regard for if it was there or not, we can +now simply write: + +```rust +let Ok(index) | Err(index) = slice.binary_search(&x); +``` + +and we will get back the `index` in any case and continue on from there. + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From ea0b3021a078b75e39600b153117083f1429c486 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 14 Feb 2018 08:52:34 -0800 Subject: [PATCH 7/7] RFC 2175 --- ...if-while-or-patterns.md => 2175-if-while-or-patterns.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename text/{0000-if-while-or-patterns.md => 2175-if-while-or-patterns.md} (98%) diff --git a/text/0000-if-while-or-patterns.md b/text/2175-if-while-or-patterns.md similarity index 98% rename from text/0000-if-while-or-patterns.md rename to text/2175-if-while-or-patterns.md index d4f9ac87b17..4a6dc664973 100644 --- a/text/0000-if-while-or-patterns.md +++ b/text/2175-if-while-or-patterns.md @@ -1,7 +1,7 @@ - Feature Name: if_while_or_patterns - Start Date: 2017-10-16 -- RFC PR: (leave this empty) -- Rust Issue: (leave this empty) +- RFC PR: https://github.com/rust-lang/rfcs/pull/2175 +- Rust Issue: https://github.com/rust-lang/rust/issues/48215 # Summary [summary]: #summary @@ -380,4 +380,4 @@ The exact syntax transformations should be deferred to the implementation. This RFC does not mandate exactly how the AST:s should be transformed, only that the or-pattern feature be supported. -There are no unresolved questions. \ No newline at end of file +There are no unresolved questions.