@@ -12,7 +12,7 @@ The new constructs are:
12
12
13
13
* An ` ? ` operator for explicitly propagating "exceptions".
14
14
15
- * A ` try ` .. ` catch ` construct for conveniently catching and handling
15
+ * A ` catch { ... } ` expression for conveniently catching and handling
16
16
"exceptions".
17
17
18
18
The idea for the ` ? ` operator originates from [ RFC PR 204] [ 204 ] by
@@ -39,10 +39,11 @@ These constructs are strict additions to the existing language, and apart from
39
39
the issue of keywords, the legality and behavior of all currently existing Rust
40
40
programs is entirely unaffected.
41
41
42
- The most important additions are a postfix ` ? ` operator for propagating
43
- "exceptions" and a ` try ` ..` catch ` block for catching and handling them. By an
44
- "exception", for now, we essentially just mean the ` Err ` variant of a ` Result ` .
45
-
42
+ The most important additions are a postfix ` ? ` operator for
43
+ propagating "exceptions" and a ` catch {..} ` expression for catching
44
+ them. By an "exception", for now, we essentially just mean the ` Err `
45
+ variant of a ` Result ` , though the Unresolved Questions includes some
46
+ discussion of extending to other types.
46
47
47
48
## ` ? ` operator
48
49
@@ -112,54 +113,31 @@ forwarding from `From`). The precise requirements for a conversion to be "like"
112
113
a subtyping coercion are an open question; see the "Unresolved questions"
113
114
section.
114
115
115
-
116
- ## ` try ` ..` catch `
117
-
118
- Like most other things in Rust, and unlike other languages that I know of,
119
- ` try ` ..` catch ` is an * expression* . If no exception is thrown in the ` try ` block,
120
- the ` try ` ..` catch ` evaluates to the value of ` try ` block; if an exception is
121
- thrown, it is passed to the ` catch ` block, and the ` try ` ..` catch ` evaluates to
122
- the value of the ` catch ` block. As with ` if ` ..` else ` expressions, the types of
123
- the ` try ` and ` catch ` blocks must therefore unify. Unlike other languages, only
124
- a single type of exception may be thrown in the ` try ` block (a ` Result ` only has
125
- a single ` Err ` type); all exceptions are always caught; and there may only be
126
- one ` catch ` block. This dramatically simplifies thinking about the behavior of
127
- exception-handling code.
128
-
129
- There are two variations on this theme:
130
-
131
- 1 . ` try { EXPR } `
132
-
133
- In this case the ` try ` block evaluates directly to a ` Result ` containing
134
- either the value of ` EXPR ` , or the exception which was thrown. For instance,
135
- ` try { foo()? } ` is essentially equivalent to ` foo() ` . This can be useful if
136
- you want to coalesce * multiple* potential exceptions -
137
- ` try { foo()?.bar()?.baz()? } ` - into a single ` Result ` , which you wish to
138
- then e.g. pass on as-is to another function, rather than analyze yourself.
139
-
140
- 2 . ` try { EXPR } catch { PAT => EXPR, PAT => EXPR, ... } `
141
-
142
- For example:
143
-
144
- try {
145
- foo()?.bar()?
146
- } catch {
147
- Red(rex) => baz(rex),
148
- Blue(bex) => quux(bex)
149
- }
150
-
151
- Here the ` catch ` performs a ` match ` on the caught exception directly, using
152
- any number of refutable patterns. This form is convenient for handling the
153
- exception in-place.
154
-
116
+ ## ` catch ` expressions
117
+
118
+ This RFC also introduces an expression form ` catch {..} ` , which serves
119
+ to "scope" the ` ? ` operator. The ` catch ` operator executes its
120
+ associated block. If no exception is thrown, then the result is
121
+ ` Ok(v) ` where ` v ` is the value of the block. Otherwise, if an
122
+ exception is thrown, then the result is ` Err(e) ` . Note that unlike
123
+ other languages, a ` catch ` block always catches all errors, and they
124
+ must all be coercable to a single type, as a ` Result ` only has a
125
+ single ` Err ` type. This dramatically simplifies thinking about the
126
+ behavior of exception-handling code.
127
+
128
+ Note that ` catch { foo()? } ` is essentially equivalent to ` foo() ` .
129
+ ` catch ` can be useful if you want to coalesce * multiple* potential
130
+ exceptions -- ` try { foo()?.bar()?.baz()? } ` -- into a single
131
+ ` Result ` , which you wish to then e.g. pass on as-is to another
132
+ function, rather than analyze yourself. (The last example could also
133
+ be expressed using a series of ` and_then ` calls.)
155
134
156
135
# Detailed design
157
136
158
137
The meaning of the constructs will be specified by a source-to-source
159
- translation. We make use of an "early exit from any block" feature which doesn't
160
- currently exist in the language, generalizes the current ` break ` and ` return `
161
- constructs, and is independently useful.
162
-
138
+ translation. We make use of an "early exit from any block" feature
139
+ which doesn't currently exist in the language, generalizes the current
140
+ ` break ` and ` return ` constructs, and is independently useful.
163
141
164
142
## Early exit from any block
165
143
@@ -250,42 +228,6 @@ are merely one way.
250
228
}.bar())
251
229
}
252
230
253
- * Construct:
254
-
255
- try {
256
- foo()?.bar()
257
- } catch {
258
- A(a) => baz(a),
259
- B(b) => quux(b)
260
- }
261
-
262
- Shallow:
263
-
264
- match (try {
265
- foo()?.bar()
266
- }) {
267
- Ok(a) => a,
268
- Err(e) => match e {
269
- A(a) => baz(a),
270
- B(b) => quux(b)
271
- }
272
- }
273
-
274
- Deep:
275
-
276
- match ('here: {
277
- Ok(match foo() {
278
- Ok(a) => a,
279
- Err(e) => break 'here Err(e.into())
280
- }.bar())
281
- }) {
282
- Ok(a) => a,
283
- Err(e) => match e {
284
- A(a) => baz(a),
285
- B(b) => quux(b)
286
- }
287
- }
288
-
289
231
The fully expanded translations get quite gnarly, but that is why it's good that
290
232
you don't have to write them!
291
233
@@ -325,9 +267,63 @@ independently.
325
267
These questions should be satisfactorally resolved before stabilizing the
326
268
relevant features, at the latest.
327
269
270
+ ## Optional ` match ` sugar
271
+
272
+ Originally, the RFC included the ability to ` match ` the errors caught
273
+ by a ` catch ` by writing ` catch { .. } match { .. } ` , which could be translated
274
+ as follows:
275
+
276
+ * Construct:
277
+
278
+ catch {
279
+ foo()?.bar()
280
+ } match {
281
+ A(a) => baz(a),
282
+ B(b) => quux(b)
283
+ }
284
+
285
+ Shallow:
286
+
287
+ match (catch {
288
+ foo()?.bar()
289
+ }) {
290
+ Ok(a) => a,
291
+ Err(e) => match e {
292
+ A(a) => baz(a),
293
+ B(b) => quux(b)
294
+ }
295
+ }
296
+
297
+ Deep:
298
+
299
+ match ('here: {
300
+ Ok(match foo() {
301
+ Ok(a) => a,
302
+ Err(e) => break 'here Err(e.into())
303
+ }.bar())
304
+ }) {
305
+ Ok(a) => a,
306
+ Err(e) => match e {
307
+ A(a) => baz(a),
308
+ B(b) => quux(b)
309
+ }
310
+ }
311
+
312
+ However, it was removed for the following reasons:
313
+
314
+ - The ` catch ` (originally: ` try ` ) keyword adds the real expressive "step up" here, the ` match ` (originally: ` catch ` ) was just sugar for ` unwrap_or ` .
315
+ - It would be easy to add further sugar in the future, once we see how ` catch ` is used (or not used) in practice.
316
+ - There was some concern about potential user confusion about two aspects:
317
+ - ` catch { } ` yields a ` Result<T,E> ` but ` catch { } match { } ` yields just ` T ` ;
318
+ - ` catch { } match { } ` handles all kinds of errors, unlike ` try/catch ` in other languages which let you pick and choose.
319
+
320
+ It may be worth adding such a sugar in the future, or perhaps a
321
+ variant that binds irrefutably and does not immediately lead into a
322
+ ` match ` block.
323
+
328
324
## Choice of keywords
329
325
330
- The RFC to this point uses the keywords ` try ` .. ` catch ` , but there are a number
326
+ The RFC to this point uses the keyword ` catch ` , but there are a number
331
327
of other possibilities, each with different advantages and drawbacks:
332
328
333
329
* ` try { ... } catch { ... } `
@@ -358,7 +354,6 @@ Among the considerations:
358
354
* Language-level backwards compatibility when adding new keywords. I'm not sure
359
355
how this could or should be handled.
360
356
361
-
362
357
## Semantics for "upcasting"
363
358
364
359
What should the contract for a ` From ` /` Into ` ` impl ` be? Are these even the right
@@ -401,12 +396,20 @@ Some further thoughts and possibilities on this matter, only as brainstorming:
401
396
(This perhaps ties into the subtyping angle: ` Ipv4Addr ` is clearly not a
402
397
supertype of ` u32 ` .)
403
398
404
-
405
399
## Forwards-compatibility
406
400
407
401
If we later want to generalize this feature to other types such as ` Option ` , as
408
402
described below, will we be able to do so while maintaining backwards-compatibility?
409
403
404
+ ## Monadic do notation
405
+
406
+ There have been many comparisons drawn between this syntax and monadic
407
+ do notation. Before stabilizing, we should determine whether we plan
408
+ to make changes to better align this feature with a possible ` do `
409
+ notation (for example, by removing the implicit ` Ok ` at the end of a
410
+ ` catch ` block). Note that such a notation would have to extend the
411
+ standard monadic bind to accommodate rich control flow like ` break ` ,
412
+ ` continue ` , and ` return ` .
410
413
411
414
# Drawbacks
412
415
@@ -466,60 +469,6 @@ described below, will we be able to do so while maintaining backwards-compatibil
466
469
467
470
This RFC doesn't propose doing so at this time, but as it would be an independently useful feature, it could be added as well.
468
471
469
- ## An additional ` catch ` form to bind the caught exception irrefutably
470
-
471
- The ` catch ` described above immediately passes the caught exception into a
472
- ` match ` block. It may sometimes be desirable to instead bind it directly to a
473
- single variable. That might look like this:
474
-
475
- try { EXPR } catch IRR-PAT { EXPR }
476
-
477
- Where ` catch ` is followed by any irrefutable pattern (as with ` let ` ).
478
-
479
- For example:
480
-
481
- try {
482
- foo()?.bar()?
483
- } catch e {
484
- let x = baz(e);
485
- quux(x, e);
486
- }
487
-
488
- While it may appear to be extravagant to provide both forms, there is reason to
489
- do so: either form on its own leads to unavoidable rightwards drift under some
490
- circumstances.
491
-
492
- The first form leads to rightwards drift if one wishes to do more complex
493
- multi-statement work with the caught exception:
494
-
495
- try {
496
- foo()?.bar()?
497
- } catch {
498
- e => {
499
- let x = baz(e);
500
- quux(x, e);
501
- }
502
- }
503
-
504
- This single case arm is quite redundant and unfortunate.
505
-
506
- The second form leads to rightwards drift if one wishes to ` match ` on the caught
507
- exception:
508
-
509
- try {
510
- foo()?.bar()?
511
- } catch e {
512
- match e {
513
- Red(rex) => baz(rex),
514
- Blue(bex) => quux(bex)
515
- }
516
- }
517
-
518
- This ` match e ` is quite redundant and unfortunate.
519
-
520
- Therefore, neither form can be considered strictly superior to the other, and it
521
- may be preferable to simply provide both.
522
-
523
472
## ` throw ` and ` throws `
524
473
525
474
It is possible to carry the exception handling analogy further and also add
0 commit comments