<div dir="ltr"><div>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:<br></div><div><br></div><div> // func foo() -> T? ...</div><div> guard let x = foo() <b>else</b> {</div><div> // the compiler does not allow control flow to escape this block</div><div> }</div><div> // control flow cannot reach here if foo() returned nil</div><div><br></div><div>Guard statements can be used with the optional form "<b>try?</b>" to provide the same functionality for throwing functions:</div><div><br></div><div> // func foo() throws -> T ...</div><div> guard let x = <b>try?</b> foo() <b>else</b> {<div> // the compiler does not allow control flow to escape this block</div> // the "error" parameter is not available here<div> }</div><div> // control flow cannot reach here if foo() threw an error</div><br></div><div>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:</div><div><br></div><div> let x: T</div><div> do {<br> x = try foo()</div><div> } catch {</div><div> // control flow can escape this block, but the compiler won't allow x to be used later if it's not initialized here</div><div> }</div><div><br></div><div><b>I propose extending guard-statements to handle errors</b> without using the optional "try?" and without a do-block, by allowing the expression to throw, and offering "catch" instead of "else":</div><div><br></div><div> // func foo() throws -> T ...</div><div> guard let x = <b>try</b> foo <b>catch</b> {</div><div> print("the error was: \(<b>error</b>)") // the "error" parameter is available here</div> // the compiler does not allow control flow to escape this block<div> }</div> // control flow cannot reach here if foo() threw an error<div><br></div><div>We could allow the same sorts of catch blocks as normal do-blocks:</div><div><br></div><div> guard let x = try foo() <b>catch let error as MyErrorType</b> {</div><div> // handle specific error; control flow must not escape<br> } <b>catch</b> {</div><div> // fallback error case; control flow must not escape</div><div> }<br><div><div><br></div><div>(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.)</div><div><br></div>Thoughts?</div><div><br></div><div>Here are some discussion topics:</div><div><br></div><div>- If Swift's error-handling mechanisms evolved into a first-class Result type, would this proposal be moot?</div><div><br></div><div>- 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 { ... }" ?</div><div><br clear="all"><div><div><div dir="ltr"><div>Jacob<br></div></div></div></div>
</div></div></div>