Guard statements interact with pattern-matching to ensure that control flow
cannot continue unless a pattern is matched. This is a very convenient way
to introduce non-optional variables from optional-valued expressions:
// func foo() -> T? ...
guard let x = foo() *else* {
// the compiler does not allow control flow to escape this block
}
// control flow cannot reach here if foo() returned nil
Guard statements can be used with the optional form "*try?*" to provide the
same functionality for throwing functions:
// func foo() throws -> T ...
guard let x = *try?* foo() *else* {
// the compiler does not allow control flow to escape this block
// the "error" parameter is not available here
}
// control flow cannot reach here if foo() threw an error
However, the error that was thrown is not recoverable inside the "else"
block. A workaround is to add extra lines of code & indentation levels to
achieve this with do+catch:
let x: T
do {
x = try foo()
} catch {
// control flow can escape this block, but the compiler won't allow
x to be used later if it's not initialized here
}
*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
We could allow the same sorts of catch blocks as normal do-blocks:
guard let x = try foo() *catch let error as MyErrorType* {
// handle specific error; control flow must not escape
} *catch* {
// fallback error case; control flow must not escape
}
(Of course, we'd want to offer sensical error message / fix-it hints when
"else" was used with a throwing statement, or when "catch" was used with a
non-throwing statement.)
Thoughts?
Here are some discussion topics:
- If Swift's error-handling mechanisms evolved into a first-class Result
type, would this proposal be moot?
- Would this make sense as a feature of pattern-matching, rather than just
"guard", so you could also do "if case let x = try foo() { ... } catch {
... }" ?
Jacob