From 2b940a209b8ffeebbbee6dd31bdbe57ec4e7babb Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 9 Mar 2018 04:10:22 +0300 Subject: [PATCH 1/8] Mini-RFC: Finalize syntax for slice patterns with subslices --- text/0000-subslice-pattern-syntax.md | 162 +++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 text/0000-subslice-pattern-syntax.md diff --git a/text/0000-subslice-pattern-syntax.md b/text/0000-subslice-pattern-syntax.md new file mode 100644 index 00000000000..5c2221586c6 --- /dev/null +++ b/text/0000-subslice-pattern-syntax.md @@ -0,0 +1,162 @@ +- Feature Name: slice_patterns +- Start Date: 2018-03-08 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Use an obvious syntax for subslice patterns - `..` and `..PAT`. +If syntactic ambiguities arise in the future, always disambiguate in favor of subslice patterns. + +# Motivation +[motivation]: #motivation + +## General motivation +Stabilization of slice pattern with subslices is currently blocked on finalizing syntax for +these subslices. +This RFC proposes a syntax for stabilization. + +## Motivation for the specific syntax + +### The shortcut form: `..` + +This form is already used in the meaning "rest of the list" in struct patterns, tuple struct +patterns and tuple patterns so it would be logical to use it for slice patterns as well. +And indeed, in unstable Rust `..` is used in this meaning since long before 1.0. + +### The full form: `..PAT` or `PAT..` + +If `..` is used in the meaning "match the subslice (`>=0` elements) and ignore it", then it's +reasonable to expect that syntax for "match the subslice to a pattern" should be some variation +on `..`. +The two simplest variations are `..PAT` and `PAT..`. + +#### Ambiguity + +The issue is that these syntaxes are ambiguous with half-bounded ranges `..END` and `BEGIN..`. +To be precise, such ranges are not currently supported in patterns, but they may be supported in +the future. + +We argue that this issue is not important and we can choose this syntax for subslice patterns +anyway. +First of all, syntactic ambiguity is not inherently bad, we see it every day in expressions like +`a + b * c`. What is important is to disambiguate it reasonably by default and have a way to +group operands in the alternative way when default disambiguation turns out to be incorrect. +In case of slice patterns the subslice interpretation seems overwhelmingly more likely, so we +can take it as a default. +There was no visible demand for implementing half-bounded ranges in patterns so far, but if they +are implemented in the future they will be able to be used in slice patterns as well, but they +will require explicit grouping with recently implemented +[parentheses in patterns](https://github.com/rust-lang/rust/pull/48500). +We can also make *some* disambiguation effort and, for example, interpret `..LITERAL` as a +range because `LITERAL` can never match a subslice. Time will show if such an effort is necessary +or not. + +If/when half-bounded ranges are supported in patterns, for better future compatibility we'll need +to reserve `..PAT` as "rest of the list" in tuples and tuple structs as well, and avoid interpreting +it as a range pattern in those positions. + +#### `..PAT` vs `PAT..` + +Originally Rust used syntax `..PAT` for subslice patterns. +In 2014 the syntax was changed to `PAT..` by [RFC 202](https://github.com/rust-lang/rfcs/pull/202). +That RFC received almost no discussion before it got merged and its motivation is no longer +relevant because arrays now use syntax `[T; N]` instead of `[T, ..N]` used in old Rust. + +Thus we are proposing to switch back to `..PAT`. +Some reasons to switch: +- Symmetry with expressions. +One of the general ideas behind patterns is that destructuring with +patterns has the same syntax as construction with expressions, if possible. +In expressions we already have something with the meaning "rest of the list" - functional record +update in struct expressions `S { field1, field2, ..remaining_fields }`. +Right now we can use `S { field1, field1, .. }` in a pattern, but can't bind the remaining fields +as a whole (by creating a new struct type on the fly, for example). It's not inconceivable that +in Rust 2525 we have such ability and it's reasonable to expect it using syntax `..remaining_fields` +symmetric to expressions. It would be good for slice patterns to be consistent with it. +Without speculations, even if `..remaining_fields` in struct expressions and `..subslice` in slice +patterns are not entirely the same thing, they are similar enough to keep them symmetric already. +- Simple disambiguation. +When we are parsing a slice pattern and see `..` we immediately know it's +a subslice and can parse following tokens as a pattern (unless they are `,` or `]`, then it's just +`..`, without an attached pattern). +With `PAT..` we need to consume the pattern first, but that pattern may be a... `RANGE_BEGIN..` +range pattern, then it means that we consumed too much and need to reinterpret the parsed tokens +somehow. It's probably possible to make this work, but it's some headache that we would like to +avoid if possible. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Subslice (aka "rest of the slice") in a slice patterns can be matched to a pattern `PAT` using +syntax `..PAT`. +`..` with the pattern omitted is a sugar for `.._` (wildcard pattern) so it means +"ignore the rest of the slice". + +Example (without `feature(match_default_bindings)`): +```rust +let v = vec![1, 2, 3]; +match v[..] { + [1, ..ref subslice, 4] => assert_eq!(subslice.len(), 1), + [5, ..ref subslice] => assert_eq!(subslice.len(), 2), + [..ref subslice, 6] => assert_eq!(subslice.len(), 2), + [x, .., y] => assert!(v.len() >= 2), + [..] => {} // Always matches +} +``` +Example (with `feature(match_default_bindings)`): +```rust +let v = vec![1, 2, 3]; +match &v[..] { + [1, ..subslice, 4] => assert_eq!(subslice.len(), 1), + [5, ..subslice] => assert_eq!(subslice.len(), 2), + [..subslice, 6] => assert_eq!(subslice.len(), 2), + [x, .., y] => assert!(v.len() >= 2), + [..] => {} // Always matches +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +Subslice in a slice patterns can be matched to a pattern `PAT` using syntax `..PAT`. +`..` with the pattern omitted is a sugar for `.._`. + +If ambiguity with some other syntactic construction arises in the future, disambiguation will be +performed in favor of the subslice pattern. + +# Drawbacks +[drawbacks]: #drawbacks + +None known. + +# Rationale and alternatives +[alternatives]: #alternatives + +The `PAT..` alternative was discussed in the motivational part of the RFC. + +More complex syntaxes derived from `..` are possible, they use additional tokens to avoid the +ambiguity with ranges, for example +[`..PAT..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-301485132), or +`.. @ PAT` or `PAT @ ..` (original comments seem to be lost by GitHub), or other similar +alternatives. +We reject these syntaxes because they only bring benefits in incredibly contrived cases using a +feature that doesn't even exist yet, but normally they only add symbolic noise. + +More radical syntax changes not keeping consistency with `..`, for example +[`[1, 2, 3, 4] ++ ref v`](https://github.com/rust-lang/rust/issues/23121#issuecomment-289220169). + +# Prior art +[prior-art]: #prior-art + +Some other languages like Scala or F# has list/array patterns, but their +syntactic choices are quite different from Rust's general style. + +"Rest of the list" in patterns was previously discussed in +[RFC 1492](https://github.com/rust-lang/rfcs/pull/1492) + +# Unresolved questions +[unresolved]: #unresolved-questions + +None known. From 001a416367ca10e995b48308e52ff9ed6529f551 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 7 Apr 2018 20:32:09 +0300 Subject: [PATCH 2/8] Address some comments --- text/0000-subslice-pattern-syntax.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/text/0000-subslice-pattern-syntax.md b/text/0000-subslice-pattern-syntax.md index 5c2221586c6..713149d355b 100644 --- a/text/0000-subslice-pattern-syntax.md +++ b/text/0000-subslice-pattern-syntax.md @@ -48,7 +48,8 @@ can take it as a default. There was no visible demand for implementing half-bounded ranges in patterns so far, but if they are implemented in the future they will be able to be used in slice patterns as well, but they will require explicit grouping with recently implemented -[parentheses in patterns](https://github.com/rust-lang/rust/pull/48500). +[parentheses in patterns](https://github.com/rust-lang/rust/pull/48500) (`[a, (..end)]`) or an +explicitly written start boundary (`[a, 0 .. end]`). We can also make *some* disambiguation effort and, for example, interpret `..LITERAL` as a range because `LITERAL` can never match a subslice. Time will show if such an effort is necessary or not. @@ -57,6 +58,11 @@ If/when half-bounded ranges are supported in patterns, for better future compati to reserve `..PAT` as "rest of the list" in tuples and tuple structs as well, and avoid interpreting it as a range pattern in those positions. +Note that ambiguity with unbounded ranges as they are used in expressions (`..`) already exists in +variant `Variant(..)` and tuple `(a, b, ..)` patterns, but it's very unlikely that the `..` syntax +will ever be used in patterns in the range meaning because it duplicates functionality of the +wildcard pattern `_`. + #### `..PAT` vs `PAT..` Originally Rust used syntax `..PAT` for subslice patterns. @@ -139,8 +145,9 @@ The `PAT..` alternative was discussed in the motivational part of the RFC. More complex syntaxes derived from `..` are possible, they use additional tokens to avoid the ambiguity with ranges, for example [`..PAT..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-301485132), or -`.. @ PAT` or `PAT @ ..` (original comments seem to be lost by GitHub), or other similar -alternatives. +[`.. @ PAT`](https://github.com/rust-lang/rust/issues/23121#issuecomment-280920062) or +[`PAT @ ..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-280906823), or other +similar alternatives. We reject these syntaxes because they only bring benefits in incredibly contrived cases using a feature that doesn't even exist yet, but normally they only add symbolic noise. @@ -150,7 +157,8 @@ More radical syntax changes not keeping consistency with `..`, for example # Prior art [prior-art]: #prior-art -Some other languages like Scala or F# has list/array patterns, but their +Some other languages like Haskell (`first_elem : rest_of_the_list`), +Scala, or F# (`first_elem :: rest_of_the_list`) has list/array patterns, but their syntactic choices are quite different from Rust's general style. "Rest of the list" in patterns was previously discussed in From 3be24a6e7d49314209ef8680b569deb82f4dd71e Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 16 Jan 2019 09:55:09 -0800 Subject: [PATCH 3/8] Postpone `..PAT`/`PAT..` patterns in favor of `IDENT @ ..` --- text/0000-subslice-pattern-syntax.md | 164 ++++++++++++++++----------- 1 file changed, 97 insertions(+), 67 deletions(-) diff --git a/text/0000-subslice-pattern-syntax.md b/text/0000-subslice-pattern-syntax.md index 713149d355b..a9bb2ae115e 100644 --- a/text/0000-subslice-pattern-syntax.md +++ b/text/0000-subslice-pattern-syntax.md @@ -6,8 +6,17 @@ # Summary [summary]: #summary -Use an obvious syntax for subslice patterns - `..` and `..PAT`. -If syntactic ambiguities arise in the future, always disambiguate in favor of subslice patterns. +Permit matching sub-slices and sub-arrays with the syntax `..`. +Binding a variable to the expression matched by a subslice pattern can be done +using the existing ` @ ` syntax, for example: + +```rust +// Binding a sub-array: +let [x, y @ .., z] = [1, 2, 3, 4]; // `y: [i32, 2] = [2, 3]` + +// Binding a sub-slice: +let [x, y @ .., z]: &[u8] = &[1, 2, 3, 4]; // `y: &[i32] = &[2, 3]` +``` # Motivation [motivation]: #motivation @@ -25,7 +34,81 @@ This form is already used in the meaning "rest of the list" in struct patterns, patterns and tuple patterns so it would be logical to use it for slice patterns as well. And indeed, in unstable Rust `..` is used in this meaning since long before 1.0. -### The full form: `..PAT` or `PAT..` +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Sub-slices and sub-arrays can be matched using `..` and ` @ ..` can be used to bind +these sub-slices and sub-arrays to an identifier. + +```rust +// Matching slices using `ref` patterns: +let v = vec![1, 2, 3]; +match v[..] { + [1, ref subslice @ .., 4] => assert_eq!(subslice.len(), 1), + [5, ref subslice @ ..] => assert_eq!(subslice.len(), 2), + [ref subslice @ .., 6] => assert_eq!(subslice.len(), 2), + [x, .., y] => assert!(v.len() >= 2), + [..] => {} // Always matches +} + +// Matching slices using default-binding-modes: +let v = vec![1, 2, 3]; +match &v[..] { + [1, subslice @ .., 4] => assert_eq!(subslice.len(), 1), + [5, subslice @ ..] => assert_eq!(subslice.len(), 2), + [subslice @ .., 6] => assert_eq!(subslice.len(), 2), + [x, .., y] => assert!(v.len() >= 2), + [..] => {} // Always matches +} + +// Matching arrays by-value: +let v = [1, 2, 3]; +match v { + [1, subarray @ .., 3] => assert_eq!(subarray, [2]), + [5, subarray @ ..] => has_type::<[i32; 2]>(subarray), + [subarray @ .., 6] => has_type::<[i32, 2]>(subarray), + [x, .., y] => has_type::<[i32, 1]>(x), + [..] => {}, +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +`..` can be used as a pattern for matching sub-slices and sub-arrays. +It is treated as a "non-reference-pattern" for the purpose of determining default-binding-modes, +and so shifts the binding mode to by-`ref` or by-`ref mut` when used to match a subsection of a +reference or mutable reference to a slice or array. + +`@` can be used to bind the result of a `..` pattern to an identifier. + + When used to match against a non-reference slice (`[u8]`), `x @ ..` would attempt to bind +by-value, which would fail in the case that users haven't enabled `feature(unsized_locals)` +(since otherwise it's not possible to bind `[u8]` to a variable directly). + +# Drawbacks +[drawbacks]: #drawbacks + +None known. + +# Rationale and alternatives +[alternatives]: #alternatives + +The `PAT..` alternative was discussed in the motivational part of the RFC. + +More complex syntaxes derived from `..` are possible, they use additional tokens to avoid the +ambiguity with ranges, for example +[`..PAT..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-301485132), or +[`.. @ PAT`](https://github.com/rust-lang/rust/issues/23121#issuecomment-280920062) or +[`PAT @ ..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-280906823), or other +similar alternatives. +We reject these syntaxes because they only bring benefits in incredibly contrived cases using a +feature that doesn't even exist yet, but normally they only add symbolic noise. + +More radical syntax changes not keeping consistency with `..`, for example +[`[1, 2, 3, 4] ++ ref v`](https://github.com/rust-lang/rust/issues/23121#issuecomment-289220169). + +### `..PAT` or `PAT..` If `..` is used in the meaning "match the subslice (`>=0` elements) and ignore it", then it's reasonable to expect that syntax for "match the subslice to a pattern" should be some variation @@ -38,9 +121,7 @@ The issue is that these syntaxes are ambiguous with half-bounded ranges `..END` To be precise, such ranges are not currently supported in patterns, but they may be supported in the future. -We argue that this issue is not important and we can choose this syntax for subslice patterns -anyway. -First of all, syntactic ambiguity is not inherently bad, we see it every day in expressions like +Syntactic ambiguity is not inherently bad. We see it every day in expressions like `a + b * c`. What is important is to disambiguate it reasonably by default and have a way to group operands in the alternative way when default disambiguation turns out to be incorrect. In case of slice patterns the subslice interpretation seems overwhelmingly more likely, so we @@ -70,7 +151,7 @@ In 2014 the syntax was changed to `PAT..` by [RFC 202](https://github.com/rust-l That RFC received almost no discussion before it got merged and its motivation is no longer relevant because arrays now use syntax `[T; N]` instead of `[T, ..N]` used in old Rust. -Thus we are proposing to switch back to `..PAT`. +This RFC originally proposed to switch back to `..PAT`. Some reasons to switch: - Symmetry with expressions. One of the general ideas behind patterns is that destructuring with @@ -92,67 +173,16 @@ range pattern, then it means that we consumed too much and need to reinterpret t somehow. It's probably possible to make this work, but it's some headache that we would like to avoid if possible. -# Guide-level explanation -[guide-level-explanation]: #guide-level-explanation - -Subslice (aka "rest of the slice") in a slice patterns can be matched to a pattern `PAT` using -syntax `..PAT`. -`..` with the pattern omitted is a sugar for `.._` (wildcard pattern) so it means -"ignore the rest of the slice". - -Example (without `feature(match_default_bindings)`): -```rust -let v = vec![1, 2, 3]; -match v[..] { - [1, ..ref subslice, 4] => assert_eq!(subslice.len(), 1), - [5, ..ref subslice] => assert_eq!(subslice.len(), 2), - [..ref subslice, 6] => assert_eq!(subslice.len(), 2), - [x, .., y] => assert!(v.len() >= 2), - [..] => {} // Always matches -} -``` -Example (with `feature(match_default_bindings)`): -```rust -let v = vec![1, 2, 3]; -match &v[..] { - [1, ..subslice, 4] => assert_eq!(subslice.len(), 1), - [5, ..subslice] => assert_eq!(subslice.len(), 2), - [..subslice, 6] => assert_eq!(subslice.len(), 2), - [x, .., y] => assert!(v.len() >= 2), - [..] => {} // Always matches -} -``` - -# Reference-level explanation -[reference-level-explanation]: #reference-level-explanation - -Subslice in a slice patterns can be matched to a pattern `PAT` using syntax `..PAT`. -`..` with the pattern omitted is a sugar for `.._`. - -If ambiguity with some other syntactic construction arises in the future, disambiguation will be -performed in favor of the subslice pattern. - -# Drawbacks -[drawbacks]: #drawbacks - -None known. - -# Rationale and alternatives -[alternatives]: #alternatives - -The `PAT..` alternative was discussed in the motivational part of the RFC. - -More complex syntaxes derived from `..` are possible, they use additional tokens to avoid the -ambiguity with ranges, for example -[`..PAT..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-301485132), or -[`.. @ PAT`](https://github.com/rust-lang/rust/issues/23121#issuecomment-280920062) or -[`PAT @ ..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-280906823), or other -similar alternatives. -We reject these syntaxes because they only bring benefits in incredibly contrived cases using a -feature that doesn't even exist yet, but normally they only add symbolic noise. +This RFC no longer includes the addition of `..PAT` or `PAT..`, but merely `..` as it results in +a smaller starting surface-area for the feature which can be expanded in the future if necessary. +The currently-proposed change is an extremely minimal addition to patterns (`..` for slices) which +already exists in other forms (e.g. tuples) and generalizes well to pattern-matching out sub-tuples, +e.g. `let (a, b @ .., c) = (1, 2, 3, 4);`. -More radical syntax changes not keeping consistency with `..`, for example -[`[1, 2, 3, 4] ++ ref v`](https://github.com/rust-lang/rust/issues/23121#issuecomment-289220169). +Additionally, `@` is more consistent with the types of patterns that would be allowable for matching +slices (only identifiers), whereas `PAT..`/`..PAT` suggest the ability to write e.g. `..(1, x)` or +`..SomeStruct { x }` sub-patterns, which wouldn't be possible since the resulting bound variables +don't form a slice (since they're spread out in memory). # Prior art [prior-art]: #prior-art From fe215c05c92ec86982efe2e8e1d6258118580172 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 26 Jan 2019 18:08:35 +0300 Subject: [PATCH 4/8] Address more comments --- text/0000-subslice-pattern-syntax.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/text/0000-subslice-pattern-syntax.md b/text/0000-subslice-pattern-syntax.md index a9bb2ae115e..e41bfc714c9 100644 --- a/text/0000-subslice-pattern-syntax.md +++ b/text/0000-subslice-pattern-syntax.md @@ -6,9 +6,9 @@ # Summary [summary]: #summary -Permit matching sub-slices and sub-arrays with the syntax `..`. +Permit matching sub-slices and sub-arrays with the syntax `..`. Binding a variable to the expression matched by a subslice pattern can be done -using the existing ` @ ` syntax, for example: +using syntax ` @ ..` similar to the existing ` @ ` syntax, for example: ```rust // Binding a sub-array: @@ -75,17 +75,20 @@ match v { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -`..` can be used as a pattern for matching sub-slices and sub-arrays. +`..` can be used as a pattern fragment for matching sub-slices and sub-arrays. It is treated as a "non-reference-pattern" for the purpose of determining default-binding-modes, and so shifts the binding mode to by-`ref` or by-`ref mut` when used to match a subsection of a reference or mutable reference to a slice or array. -`@` can be used to bind the result of a `..` pattern to an identifier. +`@` can be used to bind the result of `..` to an identifier. When used to match against a non-reference slice (`[u8]`), `x @ ..` would attempt to bind by-value, which would fail in the case that users haven't enabled `feature(unsized_locals)` (since otherwise it's not possible to bind `[u8]` to a variable directly). +`..`/`IDENT @ ..` is not a full pattern syntax, but rather a part of slice, tuple and tuple +struct pattern syntaxes. In particular, `..` is not accepted by the `pat` macro matcher. + # Drawbacks [drawbacks]: #drawbacks @@ -94,8 +97,6 @@ None known. # Rationale and alternatives [alternatives]: #alternatives -The `PAT..` alternative was discussed in the motivational part of the RFC. - More complex syntaxes derived from `..` are possible, they use additional tokens to avoid the ambiguity with ranges, for example [`..PAT..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-301485132), or @@ -198,3 +199,9 @@ syntactic choices are quite different from Rust's general style. [unresolved]: #unresolved-questions None known. + +# Future possibilities +[future-possibilities]: #future-possibilities + +Turn `..` into a full pattern syntactically accepted in any pattern position, +(including `pat` matchers in macros), but rejected semantically outside of slice and tuple patterns. From 105b6479667171ae79c2168fcf5b05c628061047 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 31 May 2019 23:15:16 +0400 Subject: [PATCH 5/8] Address most of comments --- text/0000-subslice-pattern-syntax.md | 97 +++++++++++++++++++--------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/text/0000-subslice-pattern-syntax.md b/text/0000-subslice-pattern-syntax.md index e41bfc714c9..be85e648803 100644 --- a/text/0000-subslice-pattern-syntax.md +++ b/text/0000-subslice-pattern-syntax.md @@ -41,33 +41,67 @@ Sub-slices and sub-arrays can be matched using `..` and ` @ ..` can be us these sub-slices and sub-arrays to an identifier. ```rust -// Matching slices using `ref` patterns: -let v = vec![1, 2, 3]; +// Matching slices using `ref` and `ref mut`patterns: +let mut v = vec![1, 2, 3]; match v[..] { - [1, ref subslice @ .., 4] => assert_eq!(subslice.len(), 1), - [5, ref subslice @ ..] => assert_eq!(subslice.len(), 2), - [ref subslice @ .., 6] => assert_eq!(subslice.len(), 2), + [1, ref subslice @ .., 4] => assert_eq!(subslice.len(), 1), // typeof(subslice) == &[i32] + [5, ref subslice @ ..] => assert_eq!(subslice.len(), 2), // typeof(subslice) == &[i32] + [ref subslice @ .., 6] => assert_eq!(subslice.len(), 2), // typeof(subslice) == &[i32] + [x, .., y] => assert!(v.len() >= 2), + [..] => {} // Always matches +} +match v[..] { + [1, ref mut subslice @ .., 4] => assert_eq!(subslice.len(), 1), // typeof(subslice) == &mut [i32] + [5, ref mut subslice @ ..] => assert_eq!(subslice.len(), 2), // typeof(subslice) == &mut [i32] + [ref mut subslice @ .., 6] => assert_eq!(subslice.len(), 2), // typeof(subslice) == &mut [i32] [x, .., y] => assert!(v.len() >= 2), [..] => {} // Always matches } // Matching slices using default-binding-modes: -let v = vec![1, 2, 3]; +let mut v = vec![1, 2, 3]; match &v[..] { - [1, subslice @ .., 4] => assert_eq!(subslice.len(), 1), - [5, subslice @ ..] => assert_eq!(subslice.len(), 2), - [subslice @ .., 6] => assert_eq!(subslice.len(), 2), + [1, subslice @ .., 4] => assert_eq!(subslice.len(), 1), // typeof(subslice) == &[i32] + [5, subslice @ ..] => assert_eq!(subslice.len(), 2), // typeof(subslice) == &[i32] + [subslice @ .., 6] => assert_eq!(subslice.len(), 2), // typeof(subslice) == &[i32] + [x, .., y] => assert!(v.len() >= 2), + [..] => {} // Always matches +} +match &mut v[..] { + [1, subslice @ .., 4] => assert_eq!(subslice.len(), 1), // typeof(subslice) == &mut [i32] + [5, subslice @ ..] => assert_eq!(subslice.len(), 2), // typeof(subslice) == &mut [i32] + [subslice @ .., 6] => assert_eq!(subslice.len(), 2), // typeof(subslice) == &mut [i32] [x, .., y] => assert!(v.len() >= 2), [..] => {} // Always matches } -// Matching arrays by-value: -let v = [1, 2, 3]; +// Matching slices by value (error): +let mut v = vec![1, 2, 3]; +match v[..] { + [x @ ..] => {} // ERROR cannot move out of type `[i32]`, a non-copy slice +} + +// Matching arrays by-value and by reference (explicitly or using default-binding-modes): +let mut v = [1, 2, 3]; match v { - [1, subarray @ .., 3] => assert_eq!(subarray, [2]), - [5, subarray @ ..] => has_type::<[i32; 2]>(subarray), - [subarray @ .., 6] => has_type::<[i32, 2]>(subarray), - [x, .., y] => has_type::<[i32, 1]>(x), + [1, subarray @ .., 3] => assert_eq!(subarray, [2]), // typeof(subarray) == [i32; 1] + [5, subarray @ ..] => has_type::<[i32; 2]>(subarray), // typeof(subarray) == [i32; 2] + [subarray @ .., 6] => has_type::<[i32, 2]>(subarray), // typeof(subarray) == [i32; 2] + [x, .., y] => has_type::<[i32, 1]>(x), // typeof(subarray) == [i32; 1] + [..] => {}, +} +match v { + [1, ref subarray @ .., 3] => assert_eq!(subarray, [2]), // typeof(subarray) == &[i32; 1] + [5, ref subarray @ ..] => has_type::<&[i32; 2]>(subarray), // typeof(subarray) == &[i32; 2] + [ref subarray @ .., 6] => has_type::<&[i32, 2]>(subarray), // typeof(subarray) == &[i32; 2] + [x, .., y] => has_type::<&[i32, 1]>(x), // typeof(subarray) == &[i32; 1] + [..] => {}, +} +match &mut v { + [1, subarray @ .., 3] => assert_eq!(subarray, [2]), // typeof(subarray) == &mut [i32; 1] + [5, subarray @ ..] => has_type::<&mut [i32; 2]>(subarray), // typeof(subarray) == &mut [i32; 2] + [subarray @ .., 6] => has_type::<&mut [i32, 2]>(subarray), // typeof(subarray) == &mut [i32; 2] + [x, .., y] => has_type::<&mut [i32, 1]>(x), // typeof(subarray) == &mut [i32; 1] [..] => {}, } ``` @@ -82,9 +116,8 @@ reference or mutable reference to a slice or array. `@` can be used to bind the result of `..` to an identifier. - When used to match against a non-reference slice (`[u8]`), `x @ ..` would attempt to bind -by-value, which would fail in the case that users haven't enabled `feature(unsized_locals)` -(since otherwise it's not possible to bind `[u8]` to a variable directly). +When used to match against a non-reference slice (`[u8]`), `x @ ..` would attempt to bind +by-value, which would fail due a move from a non-copy type `[u8]`. `..`/`IDENT @ ..` is not a full pattern syntax, but rather a part of slice, tuple and tuple struct pattern syntaxes. In particular, `..` is not accepted by the `pat` macro matcher. @@ -103,10 +136,10 @@ ambiguity with ranges, for example [`.. @ PAT`](https://github.com/rust-lang/rust/issues/23121#issuecomment-280920062) or [`PAT @ ..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-280906823), or other similar alternatives. -We reject these syntaxes because they only bring benefits in incredibly contrived cases using a +We reject these syntaxes because they only bring benefits in contrived cases using a feature that doesn't even exist yet, but normally they only add symbolic noise. -More radical syntax changes not keeping consistency with `..`, for example +More radical syntax changes do not keep consistency with `..`, for example [`[1, 2, 3, 4] ++ ref v`](https://github.com/rust-lang/rust/issues/23121#issuecomment-289220169). ### `..PAT` or `PAT..` @@ -118,30 +151,32 @@ The two simplest variations are `..PAT` and `PAT..`. #### Ambiguity -The issue is that these syntaxes are ambiguous with half-bounded ranges `..END` and `BEGIN..`. +The issue is that these syntaxes are ambiguous with half-bounded ranges `..END` and `BEGIN..`, +and the full range `..`. To be precise, such ranges are not currently supported in patterns, but they may be supported in the future. Syntactic ambiguity is not inherently bad. We see it every day in expressions like `a + b * c`. What is important is to disambiguate it reasonably by default and have a way to group operands in the alternative way when default disambiguation turns out to be incorrect. -In case of slice patterns the subslice interpretation seems overwhelmingly more likely, so we +In case of slice patterns the subslice interpretation seems more likely, so we can take it as a default. -There was no visible demand for implementing half-bounded ranges in patterns so far, but if they +There was very little demand for implementing half-bounded ranges in patterns so far +(see https://github.com/rust-lang/rfcs/issues/947), but if they are implemented in the future they will be able to be used in slice patterns as well, but they -will require explicit grouping with recently implemented +could require explicit grouping with recently implemented [parentheses in patterns](https://github.com/rust-lang/rust/pull/48500) (`[a, (..end)]`) or an explicitly written start boundary (`[a, 0 .. end]`). We can also make *some* disambiguation effort and, for example, interpret `..LITERAL` as a range because `LITERAL` can never match a subslice. Time will show if such an effort is necessary or not. -If/when half-bounded ranges are supported in patterns, for better future compatibility we'll need -to reserve `..PAT` as "rest of the list" in tuples and tuple structs as well, and avoid interpreting -it as a range pattern in those positions. +If/when half-bounded ranges are supported in patterns, for better future compatibility we could +decide to reserve `..PAT` as "rest of the list" in tuples and tuple structs as well, and avoid +interpreting it as a range pattern in those positions. Note that ambiguity with unbounded ranges as they are used in expressions (`..`) already exists in -variant `Variant(..)` and tuple `(a, b, ..)` patterns, but it's very unlikely that the `..` syntax +variant `Variant(..)` and tuple `(a, b, ..)` patterns, but it's unlikely that the `..` syntax will ever be used in patterns in the range meaning because it duplicates functionality of the wildcard pattern `_`. @@ -153,7 +188,7 @@ That RFC received almost no discussion before it got merged and its motivation i relevant because arrays now use syntax `[T; N]` instead of `[T, ..N]` used in old Rust. This RFC originally proposed to switch back to `..PAT`. -Some reasons to switch: +Some reasons to switch were: - Symmetry with expressions. One of the general ideas behind patterns is that destructuring with patterns has the same syntax as construction with expressions, if possible. @@ -161,7 +196,7 @@ In expressions we already have something with the meaning "rest of the list" - f update in struct expressions `S { field1, field2, ..remaining_fields }`. Right now we can use `S { field1, field1, .. }` in a pattern, but can't bind the remaining fields as a whole (by creating a new struct type on the fly, for example). It's not inconceivable that -in Rust 2525 we have such ability and it's reasonable to expect it using syntax `..remaining_fields` +in Rust 2030 we have such ability and it's reasonable to expect it using syntax `..remaining_fields` symmetric to expressions. It would be good for slice patterns to be consistent with it. Without speculations, even if `..remaining_fields` in struct expressions and `..subslice` in slice patterns are not entirely the same thing, they are similar enough to keep them symmetric already. @@ -176,7 +211,7 @@ avoid if possible. This RFC no longer includes the addition of `..PAT` or `PAT..`, but merely `..` as it results in a smaller starting surface-area for the feature which can be expanded in the future if necessary. -The currently-proposed change is an extremely minimal addition to patterns (`..` for slices) which +The currently-proposed change is a minimal addition to patterns (`..` for slices) which already exists in other forms (e.g. tuples) and generalizes well to pattern-matching out sub-tuples, e.g. `let (a, b @ .., c) = (1, 2, 3, 4);`. From 8d0281873b78783f893afc108cb7f7e019496ba9 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 1 Jun 2019 13:43:58 +0400 Subject: [PATCH 6/8] Address remaining comments --- text/0000-subslice-pattern-syntax.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/text/0000-subslice-pattern-syntax.md b/text/0000-subslice-pattern-syntax.md index be85e648803..f19a186a468 100644 --- a/text/0000-subslice-pattern-syntax.md +++ b/text/0000-subslice-pattern-syntax.md @@ -110,17 +110,30 @@ match &mut v { [reference-level-explanation]: #reference-level-explanation `..` can be used as a pattern fragment for matching sub-slices and sub-arrays. -It is treated as a "non-reference-pattern" for the purpose of determining default-binding-modes, -and so shifts the binding mode to by-`ref` or by-`ref mut` when used to match a subsection of a -reference or mutable reference to a slice or array. + +The fragment's syntax is: +``` +SUBSLICE = .. | BINDING @ .. +BINDING = ref? mut? IDENT +``` + +The subslice fragment incorporates into the full subslice syntax in the same way as the `..` +fragment incorporates into the stable tuple pattern syntax (with regards to allowed number of +subslices, trailing commas, etc). `@` can be used to bind the result of `..` to an identifier. +`..` is treated as a "non-reference-pattern" for the purpose of determining default-binding-modes, +and so shifts the binding mode to by-`ref` or by-`ref mut` when used to match a subsection of a +reference or mutable reference to a slice or array. + When used to match against a non-reference slice (`[u8]`), `x @ ..` would attempt to bind by-value, which would fail due a move from a non-copy type `[u8]`. -`..`/`IDENT @ ..` is not a full pattern syntax, but rather a part of slice, tuple and tuple +`..` is not a full pattern syntax, but rather a part of slice, tuple and tuple struct pattern syntaxes. In particular, `..` is not accepted by the `pat` macro matcher. +`BINDING @ ..` is also not a full pattern syntax, but rather a part of slice pattern syntax, so +it is not accepted by the `pat` macro matcher either. # Drawbacks [drawbacks]: #drawbacks @@ -209,8 +222,7 @@ range pattern, then it means that we consumed too much and need to reinterpret t somehow. It's probably possible to make this work, but it's some headache that we would like to avoid if possible. -This RFC no longer includes the addition of `..PAT` or `PAT..`, but merely `..` as it results in -a smaller starting surface-area for the feature which can be expanded in the future if necessary. +This RFC no longer includes the addition of `..PAT` or `PAT..`. The currently-proposed change is a minimal addition to patterns (`..` for slices) which already exists in other forms (e.g. tuples) and generalizes well to pattern-matching out sub-tuples, e.g. `let (a, b @ .., c) = (1, 2, 3, 4);`. From b9e08206dad173acb63d1b4b658b31cb18fd6e64 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 8 Jun 2019 18:06:04 +0300 Subject: [PATCH 7/8] Fix some copy-paste errors --- text/0000-subslice-pattern-syntax.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-subslice-pattern-syntax.md b/text/0000-subslice-pattern-syntax.md index f19a186a468..44cba1bd77f 100644 --- a/text/0000-subslice-pattern-syntax.md +++ b/text/0000-subslice-pattern-syntax.md @@ -87,21 +87,21 @@ match v { [1, subarray @ .., 3] => assert_eq!(subarray, [2]), // typeof(subarray) == [i32; 1] [5, subarray @ ..] => has_type::<[i32; 2]>(subarray), // typeof(subarray) == [i32; 2] [subarray @ .., 6] => has_type::<[i32, 2]>(subarray), // typeof(subarray) == [i32; 2] - [x, .., y] => has_type::<[i32, 1]>(x), // typeof(subarray) == [i32; 1] + [x, .., y] => has_type::(x), [..] => {}, } match v { [1, ref subarray @ .., 3] => assert_eq!(subarray, [2]), // typeof(subarray) == &[i32; 1] [5, ref subarray @ ..] => has_type::<&[i32; 2]>(subarray), // typeof(subarray) == &[i32; 2] [ref subarray @ .., 6] => has_type::<&[i32, 2]>(subarray), // typeof(subarray) == &[i32; 2] - [x, .., y] => has_type::<&[i32, 1]>(x), // typeof(subarray) == &[i32; 1] + [x, .., y] => has_type::<&i32>(x), [..] => {}, } match &mut v { [1, subarray @ .., 3] => assert_eq!(subarray, [2]), // typeof(subarray) == &mut [i32; 1] [5, subarray @ ..] => has_type::<&mut [i32; 2]>(subarray), // typeof(subarray) == &mut [i32; 2] [subarray @ .., 6] => has_type::<&mut [i32, 2]>(subarray), // typeof(subarray) == &mut [i32; 2] - [x, .., y] => has_type::<&mut [i32, 1]>(x), // typeof(subarray) == &mut [i32; 1] + [x, .., y] => has_type::<&mut i32>(x), [..] => {}, } ``` From a99445e39d2245393fd6caf6057c7c42477b743c Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 30 Jun 2019 23:08:12 +0200 Subject: [PATCH 8/8] RFC 2359 --- ...ce-pattern-syntax.md => 2359-subslice-pattern-syntax.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename text/{0000-subslice-pattern-syntax.md => 2359-subslice-pattern-syntax.md} (98%) diff --git a/text/0000-subslice-pattern-syntax.md b/text/2359-subslice-pattern-syntax.md similarity index 98% rename from text/0000-subslice-pattern-syntax.md rename to text/2359-subslice-pattern-syntax.md index 44cba1bd77f..160438eab85 100644 --- a/text/0000-subslice-pattern-syntax.md +++ b/text/2359-subslice-pattern-syntax.md @@ -1,7 +1,7 @@ -- Feature Name: slice_patterns +- Feature Name: `slice_patterns` - Start Date: 2018-03-08 -- RFC PR: (leave this empty) -- Rust Issue: (leave this empty) +- RFC PR: [rust-lang/rfcs#2359](https://github.com/rust-lang/rfcs/pull/2359) +- Rust Issue: [rust-lang/rust#62254](https://github.com/rust-lang/rust/issues/62254) # Summary [summary]: #summary