Skip to content

Commit 2b940a2

Browse files
committed
Mini-RFC: Finalize syntax for slice patterns with subslices
1 parent de0917d commit 2b940a2

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

text/0000-subslice-pattern-syntax.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
- Feature Name: slice_patterns
2+
- Start Date: 2018-03-08
3+
- RFC PR: (leave this empty)
4+
- Rust Issue: (leave this empty)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Use an obvious syntax for subslice patterns - `..` and `..PAT`.
10+
If syntactic ambiguities arise in the future, always disambiguate in favor of subslice patterns.
11+
12+
# Motivation
13+
[motivation]: #motivation
14+
15+
## General motivation
16+
Stabilization of slice pattern with subslices is currently blocked on finalizing syntax for
17+
these subslices.
18+
This RFC proposes a syntax for stabilization.
19+
20+
## Motivation for the specific syntax
21+
22+
### The shortcut form: `..`
23+
24+
This form is already used in the meaning "rest of the list" in struct patterns, tuple struct
25+
patterns and tuple patterns so it would be logical to use it for slice patterns as well.
26+
And indeed, in unstable Rust `..` is used in this meaning since long before 1.0.
27+
28+
### The full form: `..PAT` or `PAT..`
29+
30+
If `..` is used in the meaning "match the subslice (`>=0` elements) and ignore it", then it's
31+
reasonable to expect that syntax for "match the subslice to a pattern" should be some variation
32+
on `..`.
33+
The two simplest variations are `..PAT` and `PAT..`.
34+
35+
#### Ambiguity
36+
37+
The issue is that these syntaxes are ambiguous with half-bounded ranges `..END` and `BEGIN..`.
38+
To be precise, such ranges are not currently supported in patterns, but they may be supported in
39+
the future.
40+
41+
We argue that this issue is not important and we can choose this syntax for subslice patterns
42+
anyway.
43+
First of all, syntactic ambiguity is not inherently bad, we see it every day in expressions like
44+
`a + b * c`. What is important is to disambiguate it reasonably by default and have a way to
45+
group operands in the alternative way when default disambiguation turns out to be incorrect.
46+
In case of slice patterns the subslice interpretation seems overwhelmingly more likely, so we
47+
can take it as a default.
48+
There was no visible demand for implementing half-bounded ranges in patterns so far, but if they
49+
are implemented in the future they will be able to be used in slice patterns as well, but they
50+
will require explicit grouping with recently implemented
51+
[parentheses in patterns](https://github.com/rust-lang/rust/pull/48500).
52+
We can also make *some* disambiguation effort and, for example, interpret `..LITERAL` as a
53+
range because `LITERAL` can never match a subslice. Time will show if such an effort is necessary
54+
or not.
55+
56+
If/when half-bounded ranges are supported in patterns, for better future compatibility we'll need
57+
to reserve `..PAT` as "rest of the list" in tuples and tuple structs as well, and avoid interpreting
58+
it as a range pattern in those positions.
59+
60+
#### `..PAT` vs `PAT..`
61+
62+
Originally Rust used syntax `..PAT` for subslice patterns.
63+
In 2014 the syntax was changed to `PAT..` by [RFC 202](https://github.com/rust-lang/rfcs/pull/202).
64+
That RFC received almost no discussion before it got merged and its motivation is no longer
65+
relevant because arrays now use syntax `[T; N]` instead of `[T, ..N]` used in old Rust.
66+
67+
Thus we are proposing to switch back to `..PAT`.
68+
Some reasons to switch:
69+
- Symmetry with expressions.
70+
One of the general ideas behind patterns is that destructuring with
71+
patterns has the same syntax as construction with expressions, if possible.
72+
In expressions we already have something with the meaning "rest of the list" - functional record
73+
update in struct expressions `S { field1, field2, ..remaining_fields }`.
74+
Right now we can use `S { field1, field1, .. }` in a pattern, but can't bind the remaining fields
75+
as a whole (by creating a new struct type on the fly, for example). It's not inconceivable that
76+
in Rust 2525 we have such ability and it's reasonable to expect it using syntax `..remaining_fields`
77+
symmetric to expressions. It would be good for slice patterns to be consistent with it.
78+
Without speculations, even if `..remaining_fields` in struct expressions and `..subslice` in slice
79+
patterns are not entirely the same thing, they are similar enough to keep them symmetric already.
80+
- Simple disambiguation.
81+
When we are parsing a slice pattern and see `..` we immediately know it's
82+
a subslice and can parse following tokens as a pattern (unless they are `,` or `]`, then it's just
83+
`..`, without an attached pattern).
84+
With `PAT..` we need to consume the pattern first, but that pattern may be a... `RANGE_BEGIN..`
85+
range pattern, then it means that we consumed too much and need to reinterpret the parsed tokens
86+
somehow. It's probably possible to make this work, but it's some headache that we would like to
87+
avoid if possible.
88+
89+
# Guide-level explanation
90+
[guide-level-explanation]: #guide-level-explanation
91+
92+
Subslice (aka "rest of the slice") in a slice patterns can be matched to a pattern `PAT` using
93+
syntax `..PAT`.
94+
`..` with the pattern omitted is a sugar for `.._` (wildcard pattern) so it means
95+
"ignore the rest of the slice".
96+
97+
Example (without `feature(match_default_bindings)`):
98+
```rust
99+
let v = vec![1, 2, 3];
100+
match v[..] {
101+
[1, ..ref subslice, 4] => assert_eq!(subslice.len(), 1),
102+
[5, ..ref subslice] => assert_eq!(subslice.len(), 2),
103+
[..ref subslice, 6] => assert_eq!(subslice.len(), 2),
104+
[x, .., y] => assert!(v.len() >= 2),
105+
[..] => {} // Always matches
106+
}
107+
```
108+
Example (with `feature(match_default_bindings)`):
109+
```rust
110+
let v = vec![1, 2, 3];
111+
match &v[..] {
112+
[1, ..subslice, 4] => assert_eq!(subslice.len(), 1),
113+
[5, ..subslice] => assert_eq!(subslice.len(), 2),
114+
[..subslice, 6] => assert_eq!(subslice.len(), 2),
115+
[x, .., y] => assert!(v.len() >= 2),
116+
[..] => {} // Always matches
117+
}
118+
```
119+
120+
# Reference-level explanation
121+
[reference-level-explanation]: #reference-level-explanation
122+
123+
Subslice in a slice patterns can be matched to a pattern `PAT` using syntax `..PAT`.
124+
`..` with the pattern omitted is a sugar for `.._`.
125+
126+
If ambiguity with some other syntactic construction arises in the future, disambiguation will be
127+
performed in favor of the subslice pattern.
128+
129+
# Drawbacks
130+
[drawbacks]: #drawbacks
131+
132+
None known.
133+
134+
# Rationale and alternatives
135+
[alternatives]: #alternatives
136+
137+
The `PAT..` alternative was discussed in the motivational part of the RFC.
138+
139+
More complex syntaxes derived from `..` are possible, they use additional tokens to avoid the
140+
ambiguity with ranges, for example
141+
[`..PAT..`](https://github.com/rust-lang/rust/issues/23121#issuecomment-301485132), or
142+
`.. @ PAT` or `PAT @ ..` (original comments seem to be lost by GitHub), or other similar
143+
alternatives.
144+
We reject these syntaxes because they only bring benefits in incredibly contrived cases using a
145+
feature that doesn't even exist yet, but normally they only add symbolic noise.
146+
147+
More radical syntax changes not keeping consistency with `..`, for example
148+
[`[1, 2, 3, 4] ++ ref v`](https://github.com/rust-lang/rust/issues/23121#issuecomment-289220169).
149+
150+
# Prior art
151+
[prior-art]: #prior-art
152+
153+
Some other languages like Scala or F# has list/array patterns, but their
154+
syntactic choices are quite different from Rust's general style.
155+
156+
"Rest of the list" in patterns was previously discussed in
157+
[RFC 1492](https://github.com/rust-lang/rfcs/pull/1492)
158+
159+
# Unresolved questions
160+
[unresolved]: #unresolved-questions
161+
162+
None known.

0 commit comments

Comments
 (0)