[swift-evolution] Idea: Extend "guard" to try-statements, with a catch block

Jacob Bandes-Storch jtbandes at gmail.com
Tue Mar 8 13:37:00 CST 2016


Thanks for the feedback, Chris.

To clarify, are you suggesting that plain variable bindings would support
this too (such as "let x = try foo() catch { ... }")? I like this idea. I
can also foresee the desire for something like this:

    let x = try foo() catch {
        print(error)
        x = bar()   // by some syntax, provide a fallback value for x,
since foo() threw
        // control flow *can* escape, since x has a value
    }

Of course, you can achieve this today with "let x; do { x = try foo() }
catch { x = bar() }", or with "let x = try? foo() ?? bar()". I just wonder
if it's worth considering the possibility that this new feature would allow
control flow to escape in some cases. (After all, control flow can exit the
"catch" block we have today.) But it's also nice to be able to glance at
the code and see that, unambiguously, control flow can't escape the block.

The reason I originally suggested "guard" is readability: Seeing the word
"guard", a reader knows that control flow can't escape the else/catch
block. But I understand why guard is still necessary for working with
optionals/patterns, and I suppose seeing "try" introducing the expression
may be enough.

Do you have thoughts on whether else/catch blocks should be re-orderable?

Another question: should it work with expressions that don't bind
variables? Simply "try foo() catch { ... }" ?  (At one point I had
considered "*do* try ... catch ...", as a braceless analogue of "*do* { try
... } catch ...".)

Jacob

On Mon, Feb 29, 2016 at 10:34 PM, Chris Lattner <clattner at apple.com> wrote:

> On Feb 29, 2016, at 12:09 PM, Jacob Bandes-Storch via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> *I propose extending guard-statements to handle errors* without using the
> optional "try?" and without a do-block, by allowing the expression to
> throw, and offering "catch" instead of "else":
>
>     // func foo() throws -> T ...
>     guard let x = *try* foo *catch* {
>         print("the error was: \(*error*)")  // the "error" parameter is
> available here
>         // the compiler does not allow control flow to escape this block
>     }
>     // control flow cannot reach here if foo() threw an error
>
>
> I don’t think that this syntax makes sense, because you’re changing "guard
> let x = ” to not test an optional.  The syntax you’re searching for seems
> more consistent as a modifier on var/let itself:
>
> // func foo() throws -> T ...
> let x = try foo() catch {
>         print("the error was: \(error)")  // the "error" parameter is
> available here
>         // the compiler does not allow control flow to escape this block
> }
>
> The guard form of this would still make sense, but the existing “guard
> let” and “guard case” matching should work as it does.  For example,
> something like this should be allowed:
>
> // func bar() throws -> T?
> guard let x = try bar() else {
>         // this runs if ‘bar’ returns nil.
>         // the compiler does not allow control flow to escape this block
> } catch {
>         print("the error was: \(error)")  // the "error" parameter is
> available here
>         // the compiler does not allow control flow to escape this block
> }
>
> More generally, the “guard” form would be workable on anything that takes
> a stmt-condition.  This brings “if" and “while” into the mix.
>
> -Chris
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160308/e63e533a/attachment.html>


More information about the swift-evolution mailing list