@@ -12,7 +12,7 @@ The new constructs are:
1212
1313 * An ` ? ` operator for explicitly propagating "exceptions".
1414
15- * A ` try ` .. ` catch ` construct for conveniently catching and handling
15+ * A ` catch { ... } ` expression for conveniently catching and handling
1616 "exceptions".
1717
1818The 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
3939the issue of keywords, the legality and behavior of all currently existing Rust
4040programs is entirely unaffected.
4141
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.
4647
4748## ` ? ` operator
4849
@@ -112,54 +113,31 @@ forwarding from `From`). The precise requirements for a conversion to be "like"
112113a subtyping coercion are an open question; see the "Unresolved questions"
113114section.
114115
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.)
155134
156135# Detailed design
157136
158137The 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.
163141
164142## Early exit from any block
165143
@@ -250,42 +228,6 @@ are merely one way.
250228 }.bar())
251229 }
252230
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-
289231The fully expanded translations get quite gnarly, but that is why it's good that
290232you don't have to write them!
291233
@@ -325,9 +267,63 @@ independently.
325267These questions should be satisfactorally resolved before stabilizing the
326268relevant features, at the latest.
327269
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+
328324## Choice of keywords
329325
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
331327of other possibilities, each with different advantages and drawbacks:
332328
333329 * ` try { ... } catch { ... } `
@@ -358,7 +354,6 @@ Among the considerations:
358354 * Language-level backwards compatibility when adding new keywords. I'm not sure
359355 how this could or should be handled.
360356
361-
362357## Semantics for "upcasting"
363358
364359What 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:
401396 (This perhaps ties into the subtyping angle: ` Ipv4Addr ` is clearly not a
402397 supertype of ` u32 ` .)
403398
404-
405399## Forwards-compatibility
406400
407401If we later want to generalize this feature to other types such as ` Option ` , as
408402described below, will we be able to do so while maintaining backwards-compatibility?
409403
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 ` .
410413
411414# Drawbacks
412415
@@ -466,60 +469,6 @@ described below, will we be able to do so while maintaining backwards-compatibil
466469
467470This RFC doesn't propose doing so at this time, but as it would be an independently useful feature, it could be added as well.
468471
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-
523472## ` throw ` and ` throws `
524473
525474It is possible to carry the exception handling analogy further and also add
0 commit comments