Guard/Catch

Not sure what you mean here. A guard/catch would allow me to handle the error in the catch, something that (of course) cannot be done with "try?", so guard/else with "try?" is not a solution. And of course returning in the catch side of a do/catch is like returning in the else side of a if/else, but that's what "guard" is about, so I probably didn't understand what's your point here.

Elviro

···

Il giorno 11 lug 2017, alle ore 01:10, Christopher Kornher <ckornher@me.com> ha scritto:

FYI this works today in Xcode 9.0 beta 2 playgrounds:

class X {
    init() throws {}

    func foo() {
        print( "Things succeeded" )
    }
}

func bar() throws -> X  { return try X() }

func f()
{
    guard let x1:X = try? X(), let x2:X = try? bar() else {
        print( "Things failed ")
        return
    }

    x1.foo()
    x2.foo()
}

f()        // works

So the only unhandled case is a non-returning throwing function or init:

class X {
    init() throws {}

    func foo() {
        print( "Things succeeded" )
    }
}

func bar() throws{ let _ =  try X() }

func f()
{
    do {
        try bar()
    } catch {
        return
    }

    guard let x:X = try? X() else {
        print( "Things failed ")
        return
    }

    x.foo()
}

f()        // works

Having to call a throwing, Void method before performing the rest of a function seems like an edge case to me

On Jul 10, 2017, at 1:45 AM, Elviro Rocca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is not a sugar proposal, in the same way as "guard" is not syntactic sugar, because it requires exiting the scope on the else branch, adding expressive power and safety to the call: also, the sugary part is pretty important because it avoids nested parentheses and very clearly states that if the guard condition is not fulfilled, the execution will not reach the next lines of code. Guard is useful to push the programmer to at least consider an early return instead of branching code paths, to achieve better clarity, readability and lower complexity, and I suspect is one of the best Swift features for many people.

Also, the case that the proposal aims to cover is not an edge case at all for a lot of people, including me. Rethrowing an error is something that I almost never do, and I consider the "umbrella" do/catch at the top of the call stack an anti-pattern, but I understand that many people like it and I'm not arguing against it. I am arguing in favor of having options and not pushing a particular style onto programmers, and for my (and many people's) style, a guard/catch with forced return is an excellent idea. In fact you seem to agree on the necessity of some kind of forced-returnish catch but your elaborations don't seem (to me) much better than the proposal itself.

Dave DeLong raised the point of weird behavior in the case of a function like:

func doSomething() throws → Result? { … }

In this case, what would the type of x be?

guard let x = try doSomething() catch { /// handle and return }

Simple, it would be Optional<Result>. I don't find this confusing at all, and if the idea that just by seeing "guard let" we should expect a non-Optional is somehow diffused, I think it's better to eradicate it.

First of all, if I'm returning an optional from a throwing function, it's probably the case that I want the Optional to be there in the returned value: the only reason why I would consider doing that is if the semantics of Optional are pretty meaningful in that case. For example, when parsing a JSON in which I expect a String or null to be at a certain key:

extension String: Error {}

func parseString(in dict: [String:Any], at key: String) throws -> String? {
  guard let x = dict[key] else { throw "No value found at '\(key)' in \(dict)" }
  if let x = x as? String { return x }
  if let x = x as? NSNull { return nil }
  throw "Value at '\(key)' in \(dict) is not 'string' or 'null"
}

Thus, if I'm returning an Optional from a throwing function it means that I want to clearly distinguish the two cases, so they shouldn't be collapsed in a single call:

guard let x = try doSomething() catch { /// handle and return }
guard let x = x else { /// handle and return }

Also, if a function returns something like "Int??", a guard-let (or if-let) on the returned value of that function will still bind an "Int?", thus unwrapping only "one level" of optional. If-let and guard-let, as of today, just unwrap a single optional level, an do not guaranteed at all that the bound value is not optional.

To me guard-let (like if-let) is basically sugar for monadic binding for Optionals, with the additional expressivity granted by the forced return. I would love to see the same monadic binding structure applied to throwing functions.

Elviro

Il giorno 09 lug 2017, alle ore 01:16, Christopher Kornher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> ha scritto:

Thanks for you considerate reply. My concern over the proliferation of “sugar proposals” is a general one. This proposal has more merit and general utiliity than many others. I have never used a throwing function in a guard statement that was not itself in a throwing function, but I can see that it could possibly be common in some code. Wrapping a guard statement and all the code that uses variables set in the guard in a do/catch is sub-optimal.

On Jul 8, 2017, at 4:16 PM, Benjamin Spratling via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I’ve read your email, but haven’t digested it fully. One thing I agree with is that most functions which call throwing functions don’t actually use a do…catch block, but instead are merely marked “throws” and the error is propagated back through the stack. Once I seriously started coding functions with errors, I realized I almost always wanted my errors to reach my view-controller or my business logic so I could present separate UI if a real error occurred, and often my error message depended on the details of the error instance.

I disagree with your conclusion on this point.
The “guard” syntax is specifically designed to achieve early return (and placing code associated with early return at the point where it happens) and cleanly installing the returned value into the surrounding scope. So far it has been used to achieve early return only with optionals, true. But is that inherent to ‘guard’, or is it merely because that’s the only way it has been used? The guard does set variables that are needed in the body of the function, and that’s exactly why using guard with values returned from throwing functions makes so much sense, because it does exactly the same thing in a general sense. The “do”…”catch” structure is intentionally designed differently, to place the “happy path” in one place and place the returns in another place. I think with guard/else, we’re seeing developers who can handle less cognitive loading find it easier to reason about early return than grouping failures after the happy path. This proposal hopes to introduce that better language architecture to the catching of errors.

On Jul 8, 2017, at 4:08 PM, Christopher Kornher <ckornher@me.com <mailto:ckornher@me.com>> wrote:

I am opposed to this proposal because it muddies up the language to support what is essentially an edge case. The standard way to exit a function early because an exception is thrown is to make the function itself throw, if it is part of a larger operation. The addition of a few lines of try/catch code is not a great burden and makes the termination of an an exception very clear.
`guard` statements are generally used to set variables that are needed in the body of a function. Using them to save a few lines of exception handing code is a very different use. There is no need to mix two relatively clean syntaxes for a few edge cases and increase cognitive load one more time,

All catches don’t have to exit the outer scope, so using guard only handles a subset

It think that creating the terse try/catch for simple cases has multiple advantages:

  1) I think that it addresses your desire for a simple way to use throwing functions easily in guard statements.

  2) It avoids having to change the guard syntax to accomplish this

  3) It is useful for handling simple one line try/catch constructs in less space in a way that should not seem too foreign to Swift developers.
  
  4) It simplifies code that currently uses nested do/try/catch constructs. Even though this is rare, it introduces significant “rightward drift”.

  5) It can used to return early from void throwing functions easily. e.g. :

> 	guard try foo( ) catch { return }

  
  Multiple void throwing functions would probably be better handled by a do/catch block, but there is no danger of needing values from these functions because there are none:

> 	do {
> 		try fn1()
> 		try fn2()
> 	} catch {
> 		// keep going, return or call a non-returning function, since throw is already handled by declaring a throwing enclosing function.
> 		// No varibles are needed by the outer block because none are set
> 		// So it is not clearly a guard-like statement
> 	}	 

  I did not think of this before, but perhaps we could allow `do` to be replaced with `guard`, thereby allowing values to escape to the outer scope, while still ensuring an early exit:

> 	guard {
> 		try fn1()
> 		try fn2()
> 		let x = fn3()
> 	} catch {
> 		// Must exit
> 	} else {
> 		// Must exit
> 	}	 

I am not sure that “leaky” braces are a good idea, so perhaps some other character could be used to indicate a non-scope or whatever you want to call it:

> 	guard <your favorite character here>
> 		try fn1()
> 		try fn2()
> 		let x = fn3()
> 	 <another favorite character here> catch {
> 		// Must exit
> 	} else {
> 		// Must exit
> 	}	 

This would make the language even harder to read, so just using braces is probably a better idea.

This would change the guard syntax slightly, but is a straightforward extrapolation of do/catch and guard, I think. Of course, this could replace the existing guard syntax entirely and its use of semicolons, if we want to go that far…

Allowing this syntax only if one of the expressions throws is possibly a good backward-compatible solution that would avoid redundant guard syntaxes.

Anyway there are lot of possibilities here. We are not forced to extend the guard statement as it exists today. The current guard statement syntax was quite controversial when it was introduced and extending it may not be the best option to do what you want.

- Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

-Ben Spratling

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I don't think that "guard let x? = ..." would be syntactically correct, AFAIK for matching the pattern "let x? =", as for any other pattern, you need to use "case", in fact the following is perfectly valid:

guard case let x? = doSomething() else {
    // handle when nil
}

I think that "guard let x =" is in fact sugar for "guard case let x? =", so your example should really be:

guard case let x? = try doSomething() catch {
    // handle error
} else {
    // handle when nil
}

This would basically mean "add an extra catch when pattern-matching with a throwing function", same as:

switch try doSomething() {
case let x?:
  // handle
case nil:
  // handle
} catch {
  // handle
}

Honestly I'm still not sold in conflating the two things. I don't think it would be problematic to clearly separate cases when I'm binding the result of a throwing function (which in fact is isomorphic to a Either<T,Error>) and binding an Optional<T>.

Elviro

···

Il giorno 10 lug 2017, alle ore 10:44, David Hart <davidhart@fastmail.com> ha scritto:

On 10 Jul 2017, at 09:45, Elviro Rocca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is not a sugar proposal, in the same way as "guard" is not syntactic sugar, because it requires exiting the scope on the else branch, adding expressive power and safety to the call: also, the sugary part is pretty important because it avoids nested parentheses and very clearly states that if the guard condition is not fulfilled, the execution will not reach the next lines of code. Guard is useful to push the programmer to at least consider an early return instead of branching code paths, to achieve better clarity, readability and lower complexity, and I suspect is one of the best Swift features for many people.

Also, the case that the proposal aims to cover is not an edge case at all for a lot of people, including me. Rethrowing an error is something that I almost never do, and I consider the "umbrella" do/catch at the top of the call stack an anti-pattern, but I understand that many people like it and I'm not arguing against it. I am arguing in favor of having options and not pushing a particular style onto programmers, and for my (and many people's) style, a guard/catch with forced return is an excellent idea. In fact you seem to agree on the necessity of some kind of forced-returnish catch but your elaborations don't seem (to me) much better than the proposal itself.

Dave DeLong raised the point of weird behavior in the case of a function like:

func doSomething() throws → Result? { … }

In this case, what would the type of x be?

guard let x = try doSomething() catch { /// handle and return }

I know we can’t do much about it now, but if optional binding had used the same syntax as it does in pattern matching, we wouldn’t be having this discussion:

guard let x = try doSomething() catch {
    // handle error
}

guard let x? = doSomething() else {
    // handle when nil
}

And mixing both would be a bit cleaner because the ? would make it explicit we are doing optional binding:

guard let x? = try doSomething() catch {
    // handle error
} else {
    // handle when nil
}

Simple, it would be Optional<Result>. I don't find this confusing at all, and if the idea that just by seeing "guard let" we should expect a non-Optional is somehow diffused, I think it's better to eradicate it.

First of all, if I'm returning an optional from a throwing function, it's probably the case that I want the Optional to be there in the returned value: the only reason why I would consider doing that is if the semantics of Optional are pretty meaningful in that case. For example, when parsing a JSON in which I expect a String or null to be at a certain key:

extension String: Error {}

func parseString(in dict: [String:Any], at key: String) throws -> String? {
  guard let x = dict[key] else { throw "No value found at '\(key)' in \(dict)" }
  if let x = x as? String { return x }
  if let x = x as? NSNull { return nil }
  throw "Value at '\(key)' in \(dict) is not 'string' or 'null"
}

Thus, if I'm returning an Optional from a throwing function it means that I want to clearly distinguish the two cases, so they shouldn't be collapsed in a single call:

guard let x = try doSomething() catch { /// handle and return }
guard let x = x else { /// handle and return }

Also, if a function returns something like "Int??", a guard-let (or if-let) on the returned value of that function will still bind an "Int?", thus unwrapping only "one level" of optional. If-let and guard-let, as of today, just unwrap a single optional level, an do not guaranteed at all that the bound value is not optional.

To me guard-let (like if-let) is basically sugar for monadic binding for Optionals, with the additional expressivity granted by the forced return. I would love to see the same monadic binding structure applied to throwing functions.

Elviro

Il giorno 09 lug 2017, alle ore 01:16, Christopher Kornher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> ha scritto:

Thanks for you considerate reply. My concern over the proliferation of “sugar proposals” is a general one. This proposal has more merit and general utiliity than many others. I have never used a throwing function in a guard statement that was not itself in a throwing function, but I can see that it could possibly be common in some code. Wrapping a guard statement and all the code that uses variables set in the guard in a do/catch is sub-optimal.

On Jul 8, 2017, at 4:16 PM, Benjamin Spratling via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I’ve read your email, but haven’t digested it fully. One thing I agree with is that most functions which call throwing functions don’t actually use a do…catch block, but instead are merely marked “throws” and the error is propagated back through the stack. Once I seriously started coding functions with errors, I realized I almost always wanted my errors to reach my view-controller or my business logic so I could present separate UI if a real error occurred, and often my error message depended on the details of the error instance.

I disagree with your conclusion on this point.
The “guard” syntax is specifically designed to achieve early return (and placing code associated with early return at the point where it happens) and cleanly installing the returned value into the surrounding scope. So far it has been used to achieve early return only with optionals, true. But is that inherent to ‘guard’, or is it merely because that’s the only way it has been used? The guard does set variables that are needed in the body of the function, and that’s exactly why using guard with values returned from throwing functions makes so much sense, because it does exactly the same thing in a general sense. The “do”…”catch” structure is intentionally designed differently, to place the “happy path” in one place and place the returns in another place. I think with guard/else, we’re seeing developers who can handle less cognitive loading find it easier to reason about early return than grouping failures after the happy path. This proposal hopes to introduce that better language architecture to the catching of errors.

On Jul 8, 2017, at 4:08 PM, Christopher Kornher <ckornher@me.com <mailto:ckornher@me.com>> wrote:

I am opposed to this proposal because it muddies up the language to support what is essentially an edge case. The standard way to exit a function early because an exception is thrown is to make the function itself throw, if it is part of a larger operation. The addition of a few lines of try/catch code is not a great burden and makes the termination of an an exception very clear.
`guard` statements are generally used to set variables that are needed in the body of a function. Using them to save a few lines of exception handing code is a very different use. There is no need to mix two relatively clean syntaxes for a few edge cases and increase cognitive load one more time,

All catches don’t have to exit the outer scope, so using guard only handles a subset

It think that creating the terse try/catch for simple cases has multiple advantages:

  1) I think that it addresses your desire for a simple way to use throwing functions easily in guard statements.

  2) It avoids having to change the guard syntax to accomplish this

  3) It is useful for handling simple one line try/catch constructs in less space in a way that should not seem too foreign to Swift developers.
  
  4) It simplifies code that currently uses nested do/try/catch constructs. Even though this is rare, it introduces significant “rightward drift”.

  5) It can used to return early from void throwing functions easily. e.g. :

> 	guard try foo( ) catch { return }

  
  Multiple void throwing functions would probably be better handled by a do/catch block, but there is no danger of needing values from these functions because there are none:

> 	do {
> 		try fn1()
> 		try fn2()
> 	} catch {
> 		// keep going, return or call a non-returning function, since throw is already handled by declaring a throwing enclosing function.
> 		// No varibles are needed by the outer block because none are set
> 		// So it is not clearly a guard-like statement
> 	}	 

  I did not think of this before, but perhaps we could allow `do` to be replaced with `guard`, thereby allowing values to escape to the outer scope, while still ensuring an early exit:

> 	guard {
> 		try fn1()
> 		try fn2()
> 		let x = fn3()
> 	} catch {
> 		// Must exit
> 	} else {
> 		// Must exit
> 	}	 

I am not sure that “leaky” braces are a good idea, so perhaps some other character could be used to indicate a non-scope or whatever you want to call it:

> 	guard <your favorite character here>
> 		try fn1()
> 		try fn2()
> 		let x = fn3()
> 	 <another favorite character here> catch {
> 		// Must exit
> 	} else {
> 		// Must exit
> 	}	 

This would make the language even harder to read, so just using braces is probably a better idea.

This would change the guard syntax slightly, but is a straightforward extrapolation of do/catch and guard, I think. Of course, this could replace the existing guard syntax entirely and its use of semicolons, if we want to go that far…

Allowing this syntax only if one of the expressions throws is possibly a good backward-compatible solution that would avoid redundant guard syntaxes.

Anyway there are lot of possibilities here. We are not forced to extend the guard statement as it exists today. The current guard statement syntax was quite controversial when it was introduced and extending it may not be the best option to do what you want.

- Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

-Ben Spratling

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

We tried pattern-matching syntax in `if let` a while ago. It was unbelievably unpopular. We changed it back.

···

On Jul 10, 2017, at 1:51 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

I know we can’t do much about it now, but if optional binding had used the same syntax as it does in pattern matching, we wouldn’t be having this discussion:

guard let x = try doSomething() catch {
    // handle error
}

guard let x? = doSomething() else {
    // handle when nil
}

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

Reposting as I posted to the wrong thread in the old forum

One of the reasons I like this proposal is at a higher level we are
checking for errors and have the ability to exit early if there is an
error, I think this aligns well with what guard represents, at least to
me. Using try? effectively ignores the error so if we want convenience we
have to give up accuracy of the error handling.

It also encourages error handling, if we have a function that uses the
result of a throwing function it's very easy to do a guard on try? and
return a generic error instead of putting the function in a do/catch block.
With this proposal we can easily put the specific error handing in the
catch block.

The proposal does also changes the concept of guard somewhat, where now it
is tied directly to conditionals and guard/else is the core concept. The
proposal takes out guard as the core of and early exit statement the
result of it failing it up to how we define our handing of it.

The obvious problem is that guard let in Swift is closely associated

with

optional unwrapping. The reader is right to expect a non-optional result
with guard let regardless of the word that comes after conditional
expression.

Yes this does change that idea, but guard let is always accompanied by
else, additionally because we also use guard along with other conditionals
guard x > 0 else {} there isn't a strong tie of guard to optionals. The
interfered result type is set by the entire line much like let a = x > 0 ? "higher" : "lower"

The nesting and ceremony, to me, were part of Swift’s philosophy of

making error handling explicit. Merging catch blocks into guards saves you
maybe 3-10 lines if you intended to actually handle the error(s), otherwise
this effectively try?’s into a failable pattern match. At which point,
you have to wonder if the error-throwing function you wrote wouldn’t be
better off just returning an Optional if you’re going to discard the
semantic content of the error.

I think this proposal goes beyond convenience as we are stating that we
want the returned value to be at the same scope as code that uses it.

If we want to save lines we can do that anyway with something like:

func divide(x: Int, y: Int) throws -> Int {...}
let z: Int; do { z = try divide(x: 10, y: 5) } catch {
    print(error)
    return
}

But we always want z to be the result of divide(x:y:) having it outside
the do/catch looks like we may intend it to be set by something else. With
this proposal we are tying z to the result of divide(x:y:).

Another way could be to use a closure

let z:Int? = {
    do {
        return try divide(x: x, y: y)
    } catch {
        print(error)
        return nil
    }
}()

but then we have to use an optional in the case where divide(x:y:) does
not return one and adding another check for the optional later.

I think separating guard/else and guard/catch is a great idea because we
wouldn't have the issue of remembering what order else and catch should be
in. We can think about guard as guard/[handler]

The prior thread and in this thread there as been talk of a version without
the guard. This feels more like an extension of the try keyword, which
doesn't sound to bad to me. It handles the case of keeping the result in
the current scope, but it just doesn't group code in a exit early style.

If we're talking about this again, there was some notable further discussion here in this PR: Add a proposal for guard-catch by khanlou · Pull Request #734 · apple/swift-evolution · GitHub

I haven't had a chance to write up the meta-analysis yet, but I was hoping to post it to these forums soon.

1 Like

So, honestly I would expect the result type where there is both an optional and throwing to be an optional... but if the core team says that the return types for all guard statements have to be the same, then how about the following:

  • We automatically promote nil return to some standard type of error, and throw that if the return is nil
  • We provide "catch nil" as a sugary way to catch that error
  • The result will remove one level of optional (as it does for guard-else now)

Basically, this is similar to allowing an "else" branch, but it renames it "catch nil" to avoid confusion. It also simplifies things a bit, since as an error, it would just get caught in the general catch branch if it isn't handled specially (and things can be handled in any order).

My impression is that this discussion is going in the direction of do notations :thinking:.

I do think that the raised issue is one worth solving, the fact that one needs to nest error handling in a do/catch block but other stuff can benefit from the early exit of guard is annoying.

I wouldn't agree, that doesn't seem to me the "standard" way of doing it. That's fine when you're dealing with a callstack in the same layer, but as soon as there is a "layer" change I would recommend wrapping the errors in meaningful errors for the upper layers. One you start doing that error handling in Swift with throws becomes a pain.

And that's why I'm not super thrill about this proposal:

I'm not sure if that the right answer but I do feel like there should be a more generic pattern here to deal with errors. Swift helps with ignoring them (with try! and try?) and bubbling them up (try and throw) but as soon as you want to do something with them there is no possible nice composition features. Is probably one of the reasons why part of the community still believes a Result type is worth having. I think that Rust does nice things with the macros on this aspect.

Would be nice if someone more knowledgeable would chime in with good ideas to generalise error handling.

Sooo... any updates?

I still think it's a good idea. There are few blockers into actually getting it into the language:

  1. Finding an implementer. This is a pretty serious feature, and would require some corresponding serious C++ work. I don't have the skills or time to take it on.

  2. Syntax. If you look in the Swift Evolution PR thread, you'll see a few ideas for syntax. Some discussion needs to be held around which direction we want to go with the feature. I've laid out my arguments there:

    To me, the guard syntax is crucial, since it implies a scope exit. I think no matter what Swift construct we glom this feature onto, the idiomatic meaning of that construct is going to shift in some direction. To that end, maybe I can propose a meta-analysis of each approach and their pros and cons to the mailing list.

I haven't proposed the meta analysis of all the syntactic options, although many of them are discussed in the proposal itself, and it would be worth reading that as well.

However, none of this matters until we can find an implementer.

3 Likes

Just today was wanting to do this:

do let captureDevice = try AVCaptureDeviceInput(device: videoDevice) catch {
    delegate?.qrScanner(didFail: "Camera not available.", error: error)
    return
}

Instead of this:

let captureDevice: AVCaptureDeviceInput
do { captureDevice = try AVCaptureDeviceInput(device: videoDevice) } catch {
    delegate?.qrScanner(didFail: "Camera not available.", error: error)
    return
}
2 Likes

I think SE-0230: Flatten nested optionals resulting from `try?` is relevant here.

There's something unsafe about not having guards in catches. Consider this case:

let model: MyType
do {
    model = try JSONDecoder.default.decode(MyType, from: someData)
} catch let error as MyError {
    completion(.failure(error))
} catch {
    completion(.failure(.parseFailure(error)))
    return
}

Notice I'm missing the return in the second catch statement. Usually a catch statement means I should exit, hoping for something like this:

let model: MyType
do {
    model = try JSONDecoder.default.decode(MyType, from: someData)
} guard let error as MyError {
    completion(.failure(error)) // Compile error: 'guard' must not fall through
} guard {
    completion(.failure(.parseFailure(error)))
    return
}
1 Like

Actually if you pin an uninitialized type above the do, a compile error will catch you with an uninitialized constant, which is pretty nice.

This is incorrect. A guard expression ensures that control flow exits the current scope if the condition is not met. This is why, for clarity, it is always spelt guard...else. If you write guard let error, it would mean that you would not want to proceed unless there was an error.

True that makes sense. I think switching the do with the guard like the original proposal seems solid. It cleans up the uninitialized variable outside the expression too.