Skip to content

Commit 356f018

Browse files
committed
Expand the rest of the outline
1 parent 0812c6e commit 356f018

File tree

3 files changed

+105
-88
lines changed

3 files changed

+105
-88
lines changed

TSPL.docc/LanguageGuide/ErrorHandling.md

Lines changed: 100 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -717,58 +717,33 @@ The error handling code in the examples above
717717
always includes a default case to handle errors
718718
that don't have their own specific `catch` clause.
719719

720-
In some special cases,
721-
you might write code that's specific about what error it throws:
720+
Most Swift code doesn't specify the type for the errors it throws.
721+
However,
722+
in some special cases,
723+
you might limit code to throwing errors of only one specific type:
722724

723725
- When running code on an embedded system
724-
where dynamic allocation of memory isn't possible.
726+
that doesn't support dynamic allocation of memory.
725727
Throwing an instance `any Error` or another boxed protocol type
726-
requires allocating memory at run time to store the error.
727-
Throwing an error of a specific type instead
728-
lets Swift allocate that memory upfront.
728+
requires allocating memory at runtime to store the error.
729+
Throwing an error of a specific type
730+
lets Swift allocate that memory upfront instead.
729731

730-
- When the errors are an implementation detail of a library,
731-
or some other unit of code,
732+
- When the errors are used only within some unit of code,
733+
like a library,
732734
and aren't part of the interface to that code.
733-
Because only the library's code throws errors ---
734-
it doesn't propagate errors from other code ---
735+
Because the errors come only from the library,
736+
and not from other dependencies or the library's clients,
735737
you can make an exhaustive list of all possible failures.
736-
And because the library always handles its own errors,
737-
738-
739-
Because all of these errors are thrown within the library's code,
738+
And because these errors are an implementation detail of the library,
740739
and they're always handled within that library.
741-
Because
742-
Because the library's clients never see the errors,
743-
◊ the API surface expressively limitations don't matter
744-
745-
-
746-
* In code that only rethrows errors,
747-
especially when the throwing code comes from a closure the caller provided.
748-
(However, neither rethrows nor typed throws is a strict superset of the other.)
749-
Example: `map` in the stdlib.
750-
Xref to reference section -- this chapter doesn't discuss rethrows
751740

752-
753-
In most code,
754-
you don't specify the type for the errors it throws,
755-
implicitly throwing an error of type `any Error`
756-
However,
757-
Swift also supports
758-
some code only throws errors of a specific type.
759-
in some places your code only throws
760-
you might need to write code that throws errors of only a specific type,
761-
so it's useful to specify that type.
762-
763-
- When you throw errors from code that
764-
doesn't depend on any other throwing code,
765-
and throws only its own errors.
766-
<!-- XXX This also feels like "implementation detail errors" -->
767-
<!-- XXX TR: Does this include not depending on the stdlib? -->
768-
769-
- When you rethrow errors,
770-
if you want to preserve the error type.
771-
<!-- XXX TR: Need to motivate why you'd use rethrow vs throws(T) -->
741+
- In code that only throws errors that were thrown elsewhere,
742+
like a function that takes a closure argument
743+
and propagates any errors from that closure.
744+
For a comparison between `rethrows`
745+
and throwing a specific, generic, error type
746+
see <doc:Declarations:Rethrowing-Functions-and-Methods>.
772747

773748
For example,
774749
consider code that summarizes ratings
@@ -781,7 +756,7 @@ enum StatisticsError: Error {
781756
}
782757
```
783758

784-
To specify that a function throws only `StatisticsError`
759+
To specify that a function throws only `StatisticsError` values as its errors
785760
you write `throws(StatisticsError)` when declaring the function,
786761
instead of just writing `throws`.
787762
This syntax is also called *typed throws*
@@ -805,44 +780,54 @@ In the code above,
805780
the `summarize(_:)` function summarizes a list of ratings
806781
expressed on a scale of 1 to 3.
807782
This function throws an instance of `StatisticsError` if the input isn't valid.
808-
809-
You can use the shorthand notation `throw .noRatings`
810-
instead of writing `throw StatisticsError.noRatings`
783+
Both places in the code above that throw an error
784+
omit the type of the error
811785
because the function's error type is already defined.
786+
You can use the short form like `throw .noRatings`
787+
instead of writing `throw StatisticsError.noRatings`
788+
when throwing an error in a function like this.
812789

813-
814-
◊ throws(StatisticsError) is a subtype of throws(any Error)
815-
◊ so you can write `try summarize(...)` in a plain `throwing` context too
816-
- Most code that throws errors just writes `throws`.
817-
This is the same as writing `throws(any Error)`.
818-
However, you can write `throws(SomeErrorType)`
819-
to throw errors of a specific concrete type.
820-
821-
822-
823-
When you write a specific error type,
790+
When you write a specific error type at the start of the function,
824791
Swift checks that you don't throw any other errors.
825792
For example,
826793
if you tried to use `VendingMachineError` from examples earlier in this chapter
827794
in the `summarize(_:)` function above,
828795
that code would produce an error at compile time.
829796

830-
◊ rewrite
831-
Code that throws a single specific error type
832-
doesn't need to include the default `catch` clause ---
833-
instead, Swift verifies that every possible error value
834-
has a corresponding `catch` clause.
797+
You can call a function that uses typed throws
798+
from within a regular throwing function:
835799

800+
```swift
801+
func someThrowingFunction() -> throws {
802+
let ratings = [1, 2, 3, 2, 2, 1]
803+
try summarize(ratings)
804+
}
805+
```
806+
807+
The code above doesn't specify an error type for `someThrowingFunction()`,
808+
so it throws `any Error`.
809+
You could also write the error type explicitly as `throws(any Error)` ---
810+
the code below is equivalent to the code above:
836811

812+
```swift
813+
func someThrowingFunction() -> throws(any Error) {
814+
let ratings = [1, 2, 3, 2, 2, 1]
815+
try summarize(ratings)
816+
}
817+
```
837818

819+
In this code,
820+
`someThrowingFunction()` propagates any errors that `summarize(_:)` throws.
821+
The errors from `summarize(_:)` are always `StatisticsError` values,
822+
which is also a valid error for `someThrowingFunction()` to throw.
823+
<!-- XXX Expand on subtyping here? -->
838824

839-
In addition to specifying the error type for a function,
840-
you can also write a specific error type
841-
in a `do`-`catch` block.
825+
In addition to specifying a function's error type,
826+
you can also write a specific error type for a `do`-`catch` statement.
842827
For example:
843828

844829
```swift
845-
let ratings = [1, 2, 3, 2, 2, 1]
830+
let ratings = []
846831
do throws(StatisticsError) {
847832
try summarize(ratings)
848833
} catch {
@@ -853,33 +838,60 @@ do throws(StatisticsError) {
853838
print("Invalid rating: \(rating)")
854839
}
855840
}
856-
// Prints XXX
841+
// Prints "No ratings available"
857842
```
858843

859-
In this code, XXX
860-
844+
In this code,
845+
writing `do throws(StatisticsError)` indicates that
846+
the `do`-`catch` statement throws `StatisticsError` values as its errors.
847+
Like other `do`-`catch` statements,
848+
the `catch` clause can either handle every possible error,
849+
or it can propagate unhandled errors for some surrounding scope to handle.
850+
Here, it handles all of the errors,
851+
using a switch with one case for each enumeration value.
852+
Like other `catch` clauses that don't have a pattern,
853+
the clause matches any error
854+
and binds the error to a local constant named `error`.
855+
Because the `do`-`catch` statement throws `StatisticsError` values,
856+
`error` is a value of type `StatisticsError`.
861857

862-
XXX
863-
If a function or `do` block throws only errors of a single type,
864-
the compiler infers that as the concrete error type.
865-
You can explicitly write `throws(any Error)` to suppress that.
858+
<!-- XXX show multiple catch clauses with different patterns? -->
866859

860+
The `catch` clause above uses a switch
861+
to match and handle each possible error.
862+
If you tried to add a new case to `StatisticsError`
863+
without updating the error-handling code,
864+
Swift would give you an error
865+
because the switch wouldn't be exhaustive anymore.
866+
For a library that catches all of its own errors,
867+
you could use this approach to ensure any new errors
868+
get corresponding new code to handle them.
867869

868-
## XXX OUTLINE XXX
870+
If a function or `do` block throws only errors of a single type,
871+
Swift infers that this code is using typed throws.
872+
Using this shorter syntax,
873+
you could write the `do`-`catch` example above as follows:
869874

870-
- You can also use opaque types like `throws(some MyErrorProtocol)` --
871-
this is still "concrete" in sense that
872-
the errors are all instances of the concrete type
873-
that's hidden behind the opaque type.
874-
And there's still one specific error type.
875+
```swift
876+
let ratings = []
877+
do {
878+
try summarize(ratings)
879+
} catch {
880+
switch error {
881+
case .noRatings:
882+
print("No ratings available")
883+
case .invalidRating(let rating):
884+
print("Invalid rating: \(rating)")
885+
}
886+
}
887+
// Prints "No ratings available"
888+
```
875889

876-
- For a normal error (of boxed protocol type)
877-
the `catch` clause needs to either include a general catch/default
878-
that handles errors whose types the other clauses don't handle,
879-
or to propagate the errors it doesn't handle.
880-
For a typed error, the catch clause can be exhaustive
881-
without a default clause
882-
by handling just that specific error type.
890+
Even though the `do`-`catch` block above
891+
doesn't specify what type of error it throws,
892+
it's still understood as throwing `StatisticsError`.
893+
You can explicitly write `throws(any Error)`
894+
to avoid letting Swift infer typed throws.
883895

884896
## Specifying Cleanup Actions
885897

TSPL.docc/ReferenceManual/Declarations.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,8 @@ and a rethrowing method can satisfy a protocol requirement for a throwing method
15981598
15991599
func f<E: Error>(closure: () throws(E) -> Int) throws(E) -> Int { ... }
16001600
func g(closure: () throws -> Int) rethrows -> Int { ... }
1601+
1602+
map() from the stdlib is another example
16011603
-->
16021604

16031605
### Asynchronous Functions and Methods

TSPL.docc/ReferenceManual/Types.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,9 @@ The throw error type must conform to the `Error` protocol.
355355
Writing `throws` without specifying an type
356356
is the same as writing `throws(any Error)`.
357357
Omitting `throws` is the same as writing `throws(Never)`.
358+
The error type that a function throws
359+
can be any type that conforms to `Error`,
360+
including generic types, boxed protocol types, and opaque types.
358361

359362
The type of error that a function throws is part of that function's type,
360363
and a subtype relationship between error types

0 commit comments

Comments
 (0)