You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A common pattern in non-trivial code where static guarentees can not be fully met (e.g. I/O, network or otherwise) is to check error cases when possible before proceding,
162
+
A common pattern in non-trivial code where static guarantees can not be fully met (e.g. I/O, network or otherwise) is to check error cases when possible before proceeding,
135
163
and "return early", by constructing an error `Result` or an empty `Option`, and returning it before the "happy path" code.
136
164
137
165
This pattern serves no practical purpose to a computer, but it is helpful for humans interacting with the code.
@@ -155,17 +183,19 @@ let Some(a) = an_option else {
155
183
This is a counterpart to `if let` expressions, and the pattern matching works identically, except that the value from the pattern match
156
184
is assigned to the surrounding scope rather than the block's scope.
let-else is syntactical sugar for either `if let { assignment } else {}` or `match`, where the non-matched case diverges.
162
190
163
191
Any expression may be put into the expression position except an `if {} else {}` as explain below in [drawbacks][].
164
192
While `if {} else {}` is technically feasible this RFC proposes it be disallowed for programmer clarity to avoid an `... else {} else {}` situation.
193
+
Rust already provides us with such a restriction, [`ExpressionWithoutBlock`][expressions].
165
194
166
195
Any pattern that could be put into if-let's pattern position can be put into let-else's pattern position.
167
196
168
197
The `else` block must diverge. This could be a keyword which diverges (returns `!`), or a panic.
198
+
This likely necessitates a new subtype of `BlockExpression`, something like `BlockExpressionDiverging`.
169
199
Allowed keywords:
170
200
-`return`
171
201
-`break`
@@ -182,34 +212,39 @@ accessible as they would normally be.
182
212
"Must diverge" is an unusual requirement, which doesn't exist elsewhere in the language as of the time of writing,
183
213
and might be difficult to explain or lead to confusing errors for programmers new to this feature.
184
214
215
+
This also neccesitates a new block expression subtype, something like `BlockExpressionDiverging`.
216
+
185
217
## `let PATTERN = if {} else {} else {};`
186
218
187
219
One unfortunate combination of this feature with regular if-else expressions is the possibility of `let PATTERN = if { a } else { b } else { c };`.
188
220
This is likely to be unclear if anyone writes it, but does not pose a syntactical issue, as `let PATTERN = if y { a } else { b };` should always be
189
-
interperited as `let Enum(x) = (if y { a } else { b });` (still a compile error as there no diverging block: `error[E0005]: refutable pattern in local binding: ...`)
221
+
interpreted as `let Enum(x) = (if y { a } else { b });` (still a compile error as there no diverging block: `error[E0005]: refutable pattern in local binding: ...`)
190
222
because the compiler won't interpret it as `let PATTERN = (if y { a }) else { b };` since `()` is not an enum.
191
223
192
-
This can be overcome by making a raw if-else in the expression position a compile error and instead requring that parentheses are inserted to disambiguate:
224
+
This can be overcome by making a raw if-else in the expression position a compile error and instead requiring that parentheses are inserted to disambiguate:
193
225
`let PATTERN = (if { a } else { b }) else { c };`.
194
226
227
+
Rust already provides us with such a restriction, and so the expression can be restricted to be a [`ExpressionWithoutBlock`][expressions].
let-else attempts to be as consistent as possible to similar existing syntax.
199
233
200
-
Fundimentally it is treated as a `let` statement, necessitating an assignment and the trailing semicolon.
234
+
Fundamentally it is treated as a `let` statement, necessitating an assignment and the trailing semicolon.
201
235
202
236
Pattern matching works identically to if-let, no new "negation" pattern matching rules are introduced.
203
237
204
-
The `else` must be followed by a block, as in `if {} else {}`.
238
+
The expression can be any [`ExpressionWithoutBlock`][expressions], in order to prevent `else {} else {}` confusion, as noted in [drawbacks][#drawbacks].
239
+
240
+
The `else` must be followed by a block, as in `if {} else {}`. This else block must be diverging as the outer
241
+
context cannot be guaranteed to continue soundly without assignment, and no alternate assignment syntax is provided.
205
242
206
-
The else block must be diverging as the outer context cannot be guarenteed to continue soundly without assignment, and no alternate assignment syntax is provided.
243
+
## Alternatives
207
244
208
245
While this feature can effectively be covered by functions such `or_or`/`ok_or_else` on the `Option` and `Result` types combined with the Try operator (`?`),
209
246
such functions do not exist automatically on custom enum types and require non-obvious and non-trivial implementation, and may not be map-able
210
-
to `Option`/`Result`-style functions at all (especially for enums where the "success" varient is contextual and there are many varients).
211
-
212
-
## Alternatives
247
+
to `Option`/`Result`-style functions at all (especially for enums where the "success" variant is contextual and there are many variants).
213
248
214
249
### `let PATTERN = EXPR else return EXPR;`
215
250
@@ -230,19 +265,19 @@ This was originally suggested in the old RFC, comment at https://github.com/rust
230
265
A fall-back assignment alternate to the diverging block has been proposed multiple times in relation to this feature in the [original rfc][] and also in out-of-RFC discussions.
231
266
232
267
This RFC avoids this proposal, because there is no clear syntax to use for it which would be consistent with other existing features.
233
-
Also use-cases for having a single fall-back are much more rare and ususual, where as use cases for the diverging block are very common.
268
+
Also use-cases for having a single fall-back are much more rare and unusual, where as use cases for the diverging block are very common.
234
269
This RFC proposes that most fallback cases are sufficiently or better covered by using `match`.
235
270
236
271
An example, using a proposal to have the binding be visible and assignable from the `else`-block.
237
272
Note that this is incompatible with this RFC and could probably not be added as an extension from this RFC.
238
273
239
274
```rust
240
275
enumAnEnum {
241
-
Varient1(u32),
242
-
Varient2(String),
276
+
Variant1(u32),
277
+
Variant2(String),
243
278
}
244
279
245
-
letAnEnum::Varient1(a) =xelse {
280
+
letAnEnum::Variant1(a) =xelse {
246
281
a=42;
247
282
};
248
283
```
@@ -251,11 +286,11 @@ Another potential alternative for fall-back which could be added with an additio
251
286
252
287
```rust
253
288
enumAnEnum {
254
-
Varient1(u32),
255
-
Varient2(String),
289
+
Variant1(u32),
290
+
Variant2(String),
256
291
}
257
292
258
-
letAnEnum::Varient1(a) =xelseassigna {
293
+
letAnEnum::Variant1(a) =xelseassigna {
259
294
a=42;
260
295
};
261
296
```
@@ -265,7 +300,7 @@ let AnEnum::Varient1(a) = x else assign a {
265
300
The [old RFC][old-rfc] originally proposed this general feature via some kind of pattern negation as `if !let PAT = EXPR { BODY }`.
266
301
267
302
This RFC avoids adding any kind of new or special pattern matching rules. The pattern matching works as it does for if-let.
268
-
The general consensus in the old RFC was also that the negation syntax is much less clear than `if PATTERN = EXPR else { /* diverge */ };`,
303
+
The general consensus in the old RFC was also that the negation syntax is much less clear than `if PATTERN = EXPR_WITHOUT_BLOCK else { /* diverge */ };`,
269
304
and partway through that RFC's lifecycle it was updated to be similar to this RFC's proposed let-else syntax.
270
305
271
306
### Complete Alternative
@@ -286,9 +321,9 @@ proposal except for the choice of keywords.
286
321
The `match` alternative in particular is fairly prevalent in rust code on projects which have many possible error conditions.
287
322
288
323
The Try operator allows for an `ok_or` alternative to be used where the types are only `Option` and `Result`,
289
-
which is considered to be idomatic rust.
324
+
which is considered to be idiomatic rust.
290
325
291
-
// TODO link to examples, provide internal stistics, gather statistics from the rust compiler itself, etc.
326
+
// TODO link to examples, provide internal statistics, gather statistics from the rust compiler itself, etc.
292
327
293
328
# Unresolved questions
294
329
[unresolved-questions]: #unresolved-questions
@@ -307,11 +342,11 @@ If fall-back assignment as discussed above in [rationale-and-alternatives][] is
0 commit comments