@@ -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
765842XXX 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-
802845printSummary ([1 , 2 , 3 , 2 , 2 , 1 ])
803846printSummary ([])
804847printSummary ([1 , 100 ])
0 commit comments