-
Notifications
You must be signed in to change notification settings - Fork 204
Update error handling for typed throws [SE-0413] #296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
2140ed7
10b2abf
7ecd269
efefc23
7e76588
29cadb2
16a4135
d724f48
caf42a5
b99d8f4
ac8a86d
3c2816a
3ea9edd
a029497
0812c6e
356f018
4baad40
a0ee3a9
2fe85ff
66287ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -699,6 +699,204 @@ let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg") | |||||||||||||
| ``` | ||||||||||||||
| --> | ||||||||||||||
|
|
||||||||||||||
| ## Specifying the Error Type | ||||||||||||||
|
|
||||||||||||||
| All of the examples above use the most common kind of error handling, | ||||||||||||||
| where the errors that your code throws | ||||||||||||||
| can be values of any type that conforms to the `Error` protocol. | ||||||||||||||
| This approach matches the reality that | ||||||||||||||
| you don't know ahead of time every error that could happen | ||||||||||||||
| while the code is running, | ||||||||||||||
| especially when propagating errors thrown somewhere else. | ||||||||||||||
| This approach also reflects the fact that errors can change over time. | ||||||||||||||
| New versions of a library --- | ||||||||||||||
| including libraries that your dependencies use --- | ||||||||||||||
| can throw new errors, | ||||||||||||||
| and the rich complexity of real-world user configurations | ||||||||||||||
| can expose failure modes that weren't visible during development or testing. | ||||||||||||||
| The error handling code in the examples above | ||||||||||||||
| always includes a default case to handle errors | ||||||||||||||
| that don't have a specific `catch` clause. | ||||||||||||||
|
|
||||||||||||||
| Most Swift code doesn't specify the type for the errors it throws. | ||||||||||||||
| However, | ||||||||||||||
| you might limit code to throwing errors of only one specific type | ||||||||||||||
| in the following special cases: | ||||||||||||||
|
|
||||||||||||||
| - When running code on an embedded system | ||||||||||||||
| that doesn't support dynamic allocation of memory. | ||||||||||||||
| Throwing an instance of `any Error` or another boxed protocol type | ||||||||||||||
| requires allocating memory at runtime to store the error. | ||||||||||||||
| In contrast, | ||||||||||||||
| throwing an error of a specific type | ||||||||||||||
| lets Swift allocate that memory upfront. | ||||||||||||||
|
||||||||||||||
| lets Swift allocate that memory upfront. | |
| lets Swift avoid heap allocation for errors. |
rebeccateibloom marked this conversation as resolved.
Show resolved
Hide resolved
rebeccateibloom marked this conversation as resolved.
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to mention that this is generic code here. We could say "in generic code", or do something like this:
| - In code that throws only errors that were thrown elsewhere, | |
| like a function that takes a closure argument | |
| and propagates any errors from that closure. | |
| - In code that only propagates errors described by generic parameters, | |
| like a function that takes a closure argument | |
| and propagates any errors from that closure. |
rebeccateibloom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need to expand on subtyping. However, I would like to describe the Never case, because it's an important part of understanding how typed throws fits in the type system. For example, it could be something like this:
Just like you can write a function that never returns with a return type of
Never, you can write a function that never throws withthrows(Never):
```swift
func nonThrowingFunction() throws(Never) {
// ...
}
```
This function cannot throw because it is impossible to create a value of type
Neverto throw.
rebeccateibloom marked this conversation as resolved.
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you have to show multiple catch clauses specifically. It falls out from other sections.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1465,13 +1465,25 @@ func <#function name#>(<#parameters#>) throws -> <#return type#> { | |
| } | ||
| ``` | ||
|
|
||
| A function that throws a specific error type has the following form: | ||
|
|
||
| ```swift | ||
| func <#function name#>(<#parameters#>) throws(<#error type#>) -> <#return type#> { | ||
| <#statements#> | ||
| } | ||
| ``` | ||
|
|
||
| Calls to a throwing function or method must be wrapped in a `try` or `try!` expression | ||
| (that is, in the scope of a `try` or `try!` operator). | ||
|
|
||
| The `throws` keyword is part of a function's type, | ||
| and nonthrowing functions are subtypes of throwing functions. | ||
| As a result, you can use a nonthrowing function | ||
| A function's type includes whether it can throw an error | ||
| and what type of error it throws. | ||
| This subtype relationship means, for example, you can use a nonthrowing function | ||
| in a context where a throwing one is expected. | ||
| For more information about the type of a throwing function, | ||
| see <doc:Types#Function-Type>. | ||
| For examples of working with errors that have explicit types, | ||
| see <doc:ErrorHandling#Specifying-a-Concrete-Error-Type>. | ||
|
|
||
| You can't overload a function based only on whether the function can throw an error. | ||
| That said, | ||
|
|
@@ -1582,6 +1594,14 @@ and a throwing method can't satisfy a protocol requirement for a rethrowing meth | |
| That said, a rethrowing method can override a throwing method, | ||
| and a rethrowing method can satisfy a protocol requirement for a throwing method. | ||
|
|
||
| <!-- XXX comparison between rethrows and generic typed throws | ||
|
||
|
|
||
| func f<E: Error>(closure: () throws(E) -> Int) throws(E) -> Int { ... } | ||
| func g(closure: () throws -> Int) rethrows -> Int { ... } | ||
|
|
||
| map() from the stdlib is another example | ||
| --> | ||
|
|
||
| ### Asynchronous Functions and Methods | ||
|
|
||
| Functions and methods that run asynchronously must be marked with the `async` keyword. | ||
|
|
@@ -1660,7 +1680,7 @@ but the new method must preserve its return type and nonreturning behavior. | |
| > *function-head* → *attributes*_?_ *declaration-modifiers*_?_ **`func`** \ | ||
| > *function-name* → *identifier* | *operator* | ||
| > | ||
| > *function-signature* → *parameter-clause* **`async`**_?_ **`throws`**_?_ *function-result*_?_ \ | ||
| > *function-signature* → *parameter-clause* **`async`**_?_ *throws-clause*_?_ *function-result*_?_ \ | ||
| > *function-signature* → *parameter-clause* **`async`**_?_ **`rethrows`** *function-result*_?_ \ | ||
| > *function-result* → **`->`** *attributes*_?_ *type* \ | ||
| > *function-body* → *code-block* | ||
|
|
@@ -2558,7 +2578,7 @@ See also <doc:Declarations#Initializer-Declaration>. | |
|
|
||
| > Grammar of a protocol initializer declaration: | ||
| > | ||
| > *protocol-initializer-declaration* → *initializer-head* *generic-parameter-clause*_?_ *parameter-clause* **`throws`**_?_ *generic-where-clause*_?_ \ | ||
| > *protocol-initializer-declaration* → *initializer-head* *generic-parameter-clause*_?_ *parameter-clause* *throws-clause*_?_ *generic-where-clause*_?_ \ | ||
| > *protocol-initializer-declaration* → *initializer-head* *generic-parameter-clause*_?_ *parameter-clause* **`rethrows`** *generic-where-clause*_?_ | ||
|
|
||
| ### Protocol Subscript Declaration | ||
|
|
@@ -2903,7 +2923,7 @@ see <doc:Initialization#Failable-Initializers>. | |
|
|
||
| > Grammar of an initializer declaration: | ||
| > | ||
| > *initializer-declaration* → *initializer-head* *generic-parameter-clause*_?_ *parameter-clause* **`async`**_?_ **`throws`**_?_ *generic-where-clause*_?_ *initializer-body* \ | ||
| > *initializer-declaration* → *initializer-head* *generic-parameter-clause*_?_ *parameter-clause* **`async`**_?_ *throws-clause*_?_ *generic-where-clause*_?_ *initializer-body* \ | ||
| > *initializer-declaration* → *initializer-head* *generic-parameter-clause*_?_ *parameter-clause* **`async`**_?_ **`rethrows`** *generic-where-clause*_?_ *initializer-body* \ | ||
| > *initializer-head* → *attributes*_?_ *declaration-modifiers*_?_ **`init`** \ | ||
| > *initializer-head* → *attributes*_?_ *declaration-modifiers*_?_ **`init`** **`?`** \ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -729,6 +729,9 @@ throw <#expression#> | |
|
|
||
| The value of the *expression* must have a type that conforms to | ||
| the `Error` protocol. | ||
| If the `do` statement or function that contains the `throw` statement | ||
| declares the type of errors it throws, | ||
| the value of the *expression* must be an instance of that type. | ||
rebeccateibloom marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| For an example of how to use a `throw` statement, | ||
| see <doc:ErrorHandling#Propagating-Errors-Using-Throwing-Functions> | ||
|
|
@@ -873,6 +876,47 @@ do { | |
| } | ||
| ``` | ||
|
|
||
| A `do` statement can optionally specify the type of error it throws, | ||
| which has the following form: | ||
|
|
||
| ```swift | ||
| do throws(<#type#>) { | ||
| try <#expression#> | ||
| } catch <#pattern> { | ||
| <#statements#> | ||
| } catch { | ||
| <#statements#> | ||
| } | ||
| ``` | ||
|
|
||
| <!-- XXX Is "do throws { }" allowed? --> | ||
|
||
|
|
||
| If the `do` statement includes a `throws` clause, | ||
| the `do` block can throw errors of only the specified *type*. | ||
| The *type* must be | ||
| a concrete type that conforms to the `Error` protocol, | ||
| an opaque type that conforms to the `Error` protocol, | ||
| or the boxed protocol type `any Error`. | ||
| If the `do` statement doesn't specify the type of error it throws, | ||
| Swift infers the error type as follows: | ||
|
|
||
| - If every `throws` statement and `try` expression in the `do` code block | ||
| is nested inside of an exhaustive error-handling mechanism, | ||
| then Swift infers that the `do` statement is nonthrowing. | ||
|
|
||
| - If the `do` code block contains code that throws | ||
| errors of only a single type | ||
amartini51 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| outside of exhaustive error handling, | ||
| then Swift infers that the `do` statement throws that concrete error type. | ||
|
|
||
| - If the `do` code block contains code that throws | ||
| errors of more than a single type | ||
| outside of exhaustive error handling, | ||
| then Swift infers that the `do` statement throws `any Error`. | ||
|
|
||
| For more information about working with errors that have explicit types, | ||
| see <doc:ErrorHandling#Specifying-a-Concrete-Error-Type>. | ||
|
|
||
| If any statement in the `do` code block throws an error, | ||
| program control is transferred | ||
| to the first `catch` clause whose pattern matches the error. | ||
|
|
@@ -914,7 +958,7 @@ see <doc:ErrorHandling#Handling-Errors>. | |
|
|
||
| > Grammar of a do statement: | ||
| > | ||
| > *do-statement* → **`do`** *code-block* *catch-clauses*_?_ \ | ||
| > *do-statement* → **`do`** *throws-clause*_?_ *code-block* *catch-clauses*_?_ \ | ||
| > *catch-clauses* → *catch-clause* *catch-clauses*_?_ \ | ||
| > *catch-clause* → **`catch`** *catch-pattern-list*_?_ *code-block* \ | ||
| > *catch-pattern-list* → *catch-pattern* | *catch-pattern* **`,`** *catch-pattern-list* \ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"This approach" starts both this sentence and the prior one. Perhaps use this instead?