Skip to content

Commit a029497

Browse files
committed
Start expanding the outline
1 parent 3ea9edd commit a029497

File tree

1 file changed

+103
-60
lines changed

1 file changed

+103
-60
lines changed

TSPL.docc/LanguageGuide/ErrorHandling.md

Lines changed: 103 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -699,47 +699,124 @@ let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
699699
```
700700
-->
701701

702-
## Specifying a Concrete Error Type
702+
## Specifying the Error Type
703+
704+
All of the examples above use the most common kind of error handling,
705+
where the errors that your code throws
706+
can be values of any type that conforms to the `Error` protocol.
707+
This approach matches the reality that
708+
you don't know ahead of time every error that could happen
709+
while the code is running,
710+
especially when propagating errors thrown by code you didn't write.
711+
New versions of a library can throw new errors ---
712+
including libraries used by your dependencies ---
713+
and the rich complexity of real-world user configurations
714+
can expose failure modes that weren't visible during development or testing.
715+
The error handling code in the examples above
716+
always includes a default case to handle errors
717+
that don't have their own specific `catch` clause.
718+
719+
However,
720+
some code throws error of only a specific type,
721+
so it's useful to specify that type.
722+
723+
- When you encapsulate errors within some unit of code,
724+
like a library,
725+
if using a specific error type is useful for you.
726+
In this case,
727+
the library always handles all of its own errors.
728+
This kind of error is an implementation detail of the library.
729+
730+
- When targeting an embedded system
731+
where dynamic allocation of memory isn't possible.
732+
Swift needs to allocate memory at run time
733+
when throwing an instance `any Error` or other boxed protocol types.
734+
735+
- When you throw errors from code that
736+
doesn't depend on any other throwing code,
737+
and throws only its own errors.
738+
<!-- XXX This also feels like "implementation detail errors" -->
739+
<!-- XXX TR: Does this include not depending on the stdlib? -->
740+
741+
- When you rethrow errors,
742+
if you want to preserve the error type.
743+
<!-- XXX TR: Need to motivate why you'd use rethrow vs throws(T) -->
703744

704-
XXX OUTLINE XXX
745+
For example,
746+
consider code that summarizes ratings
747+
and uses the following error type:
748+
749+
```swift
750+
enum StatisticsError: Error {
751+
case noRatings
752+
case invalidRating(Int)
753+
}
754+
```
755+
756+
To specify that a function throws only `StatisticsError`
757+
you write `throws(StatisticsError)` when declaring the function.
758+
Because you write a type after `throws`
759+
this syntax is also called *typed throws*.
760+
761+
```swift
762+
func summarize(_ ratings: [Int]) throws(StatisticsError) {
763+
guard !ratings.isEmpty else { throw .noRatings }
764+
765+
var counts = [1: 0, 2: 0, 3: 0]
766+
for rating in ratings {
767+
guard rating > 0 && rating <= 3 else { throw .invalidRating(rating) }
768+
counts[rating]! += 1
769+
}
770+
771+
print("One star:", counts[1]!)
772+
print("Two stars:", counts[2]!)
773+
print("Three stars:", counts[3]!)
774+
775+
print("*", counts[1]!, "-- **", counts[2]!, "-- ***", counts[3]!)
776+
}
777+
```
778+
779+
<!-- XXX pick one flavor of summary above -->
780+
781+
When you write a specific error type,
782+
Swift checks at compile time that you don't throw any other errors.
783+
784+
785+
Code that throws a single specific error type
786+
doesn't include the default `catch` clause ---
787+
instead, Swift verifies that every possible error value
788+
has a corresponding `catch` clause.
789+
790+
```swift
791+
func printSummary(_ ratings: [Int]) {
792+
do throws(StatisticsError) {
793+
try summarize(ratings)
794+
} catch {
795+
switch error {
796+
case .noRatings:
797+
print("No ratings available")
798+
case .invalidRating(let rating):
799+
print("Invalid rating: \(rating)")
800+
}
801+
}
802+
}
803+
```
804+
805+
## XXX OUTLINE XXX
705806

706807
- Most code that throws errors just writes `throws`.
707808
This is the same as writing `throws(any Error)`.
708809
However, you can write `throws(SomeErrorType)`
709810
to throw errors of a specific concrete type.
710811

711-
- Because you write a type after `throws`
712-
this syntax is also called "typed throws".
713-
714-
- A boxed protocol (aka existential) type matches the reality of most code.
715-
At compile time, you don't know every possible way things could go wrong.
716-
In particular, some errors will come from calling other throwing functions.
717-
This is why most code uses a boxed protocol type as its error type.
718-
719812
- In contrast, a concrete error type is useful in some special circumstances:
720813

721-
* When the code that throws errors
722-
and the code that handles those errors
723-
are all part of the same larger unit,
724-
like a package or module or library.
725-
These errors are an implementation detail
726-
that users of the library don't see or recover from.
727-
728814
* In code that only rethrows errors,
729815
especially when the throwing code comes from a closure the caller provided.
730816
(However, neither rethrows nor typed throws is a strict superset of the other.)
731817
Example: `map` in the stdlib.
732818
Xref to reference section -- this chapter doesn't discuss rethrows
733819

734-
* In an environment where runtime memory allocation isn't possible,
735-
like a constrained embedded system.
736-
Throwing an instance of `any Error` requires allocation.
737-
The type isn't known at compile time -- allocation happens at run time.
738-
(Aside: The performance cost of allocation is small.)
739-
740-
* In code that has no dependencies, and only ever throws its own errors.
741-
TR: Does this include not depending on the stdlib?
742-
743820
- You can also use opaque types like `throws(some MyErrorProtocol)` --
744821
this is still "concrete" in sense that
745822
the errors are all instances of the concrete type
@@ -765,40 +842,6 @@ XXX OUTLINE XXX
765842
XXX RUNNING EXAMPLE XXX
766843

767844
```swift
768-
enum StatisticsError: Error {
769-
case noRatings
770-
case invalidRating(Int)
771-
}
772-
773-
func summarize(_ ratings: [Int]) throws(StatisticsError) {
774-
guard !ratings.isEmpty else { throw .noRatings }
775-
776-
var counts = [1: 0, 2: 0, 3: 0]
777-
for rating in ratings {
778-
guard rating > 0 && rating <= 3 else { throw .invalidRating(rating) }
779-
counts[rating]! += 1
780-
}
781-
782-
print("One star:", counts[1]!)
783-
print("Two stars:", counts[2]!)
784-
print("Three stars:", counts[3]!)
785-
786-
print("*", counts[1]!, "-- **", counts[2]!, "-- ***", counts[3]!)
787-
}
788-
789-
func printSummary(_ ratings: [Int]) {
790-
do throws(StatisticsError) {
791-
try summarize(ratings)
792-
} catch {
793-
switch error {
794-
case .noRatings:
795-
print("No ratings available")
796-
case .invalidRating(let rating):
797-
print("Invalid rating: \(rating)")
798-
}
799-
}
800-
}
801-
802845
printSummary([1, 2, 3, 2, 2, 1])
803846
printSummary([])
804847
printSummary([1, 100])

0 commit comments

Comments
 (0)