@@ -8,20 +8,20 @@ manipulating recoverable error conditions.
8
8
Error handling is a well-trod path, with many different approaches in
9
9
other languages, many of them problematic in various ways. We believe
10
10
that our approach provides an elegant solution, drawing on the lessons
11
- we\ ' ve learned from other languages and fixing or avoiding some of the
11
+ we've learned from other languages and fixing or avoiding some of the
12
12
pitfalls. The result is expressive and concise while still feeling
13
13
explicit, safe, and familiar; and we believe it will work beautifully
14
14
with the Cocoa APIs.
15
15
16
- We\ ' re intentionally not using the term \ " exception handling\ " , which
16
+ We're intentionally not using the term "exception handling", which
17
17
carries a lot of connotations from its use in other languages. Our
18
18
proposal has some similarities to the exceptions systems in those
19
19
languages, but it also has a lot of important differences.
20
20
21
21
## Kinds of Error
22
22
23
- What exactly is an \ " error\ " ? There are many possible error conditions,
24
- and they don\ ' t all make sense to handle in exactly the same way,
23
+ What exactly is an "error"? There are many possible error conditions,
24
+ and they don't all make sense to handle in exactly the same way,
25
25
because they arise in different circumstances and programmers have to
26
26
react to them differently.
27
27
@@ -30,22 +30,22 @@ severity:
30
30
31
31
A ** simple domain error** arises from an operation that can fail in some
32
32
obvious way and which is often invoked speculatively. Parsing an integer
33
- from a string is a really good example. The client doesn\ ' t need a
33
+ from a string is a really good example. The client doesn't need a
34
34
detailed description of the error and will usually want to handle the
35
35
error immediately. These errors are already well-modeled by returning an
36
- optional value; we don\ ' t need a more complex language solution for
36
+ optional value; we don't need a more complex language solution for
37
37
them.
38
38
39
39
A ** recoverable error** arises from an operation which can fail in
40
40
complex ways, but whose errors can be reasonably anticipated in advance.
41
41
Examples including opening a file or reading from a network connection.
42
- These are the kinds of errors that Apple\ ' s APIs use NSError for today,
42
+ These are the kinds of errors that Apple's APIs use NSError for today,
43
43
but there are close analogues in many other APIs, such as ` errno ` in
44
44
POSIX.
45
45
46
46
Ignoring this kind of error is usually a bad idea, and it can even be
47
47
dangerous (e.g. by introducing a security hole). Developers should be
48
- strongly encouraged to write code that handles the error. It\ ' s common
48
+ strongly encouraged to write code that handles the error. It's common
49
49
for developers to want to handle errors from different operations in the
50
50
same basic way, either by reporting the error to the user or passing the
51
51
error back to their own clients.
@@ -54,7 +54,7 @@ These errors will be the focus of this proposal.
54
54
55
55
The final two classes of error are outside the scope of this proposal. A
56
56
** universal error** is theoretically recoverable, but by its nature the
57
- language can\ ' t help the programmer anticipate where it will come from.
57
+ language can't help the programmer anticipate where it will come from.
58
58
A ** logic failure** arises from a programmer mistake and should not be
59
59
recoverable at all. In our system, these kinds of errors are reported
60
60
either with Objective-C/C++ exceptions or simply by logging a message
@@ -77,13 +77,13 @@ Notably, the approach preserves these advantages of this convention:
77
77
and a simple inspection reveals how the function reacts to the
78
78
error.
79
79
- Throwing an error provides similar performance to allocating an
80
- error and returning it \- - it isn\ ' t an expensive, table-based stack
80
+ error and returning it \- - it isn't an expensive, table-based stack
81
81
unwinding process.
82
82
- Cocoa APIs using standard ` NSError ` patterns can be imported into
83
83
this world automatically. Other common patterns (e.g. ` CFError ` ,
84
84
` errno ` ) can be added to the model in future versions of Swift.
85
85
86
- In addition, we feel that this design improves on Objective-C\ ' s error
86
+ In addition, we feel that this design improves on Objective-C's error
87
87
handling approach in a number of ways:
88
88
89
89
- It eliminates a lot of boilerplate control-flow code for propagating
@@ -98,25 +98,25 @@ exception handling. We considered intentionally using different terms
98
98
(like ` raise ` / ` handle ` ) to try to distinguish our approach from other
99
99
languages. However, by and large, error propagation in this proposal
100
100
works like it does in exception handling, and people are inevitably
101
- going to make the connection. Given that, we couldn\ ' t find a compelling
101
+ going to make the connection. Given that, we couldn't find a compelling
102
102
reason to deviate from the ` throw ` / ` catch ` legacy.
103
103
104
104
This document just contains the basic proposal and will be very light on
105
105
rationale. We considered many different languages and programming
106
- environments as part of making this proposal, and there\ ' s an extensive
106
+ environments as part of making this proposal, and there's an extensive
107
107
discussion of them in the separate rationale document. For example, that
108
- document explains why we don\ ' t simply allow all functions to throw, why
109
- we don\ ' t propagate errors using simply an ` ErrorOr<T> ` return type, and
110
- why we don\ ' t just make error propagation part of a general monad
111
- feature. We encourage you to read that rationale if you\ ' re interested
108
+ document explains why we don't simply allow all functions to throw, why
109
+ we don't propagate errors using simply an ` ErrorOr<T> ` return type, and
110
+ why we don't just make error propagation part of a general monad
111
+ feature. We encourage you to read that rationale if you're interested
112
112
in understanding why we made the decisions we did.
113
113
114
- With that out of the way, let\ ' s get to the details of the proposal.
114
+ With that out of the way, let's get to the details of the proposal.
115
115
116
116
## Typed propagation
117
117
118
118
Whether a function can throw is part of its type. This applies to all
119
- functions, whether they\ ' re global functions, methods, or closures.
119
+ functions, whether they're global functions, methods, or closures.
120
120
121
121
By default, a function cannot throw. The compiler statically enforces
122
122
this: anything the function does which can throw must appear in a
@@ -126,7 +126,7 @@ A function can be declared to throw by writing `throws` on the function
126
126
declaration or type:
127
127
128
128
``` swift
129
- func foo () -> Int { // This function is not permitted to throw.
129
+ func foo () -> Int { // This function is not permitted to throw.
130
130
func bar () throws -> Int { // This function is permitted to throw.
131
131
```
132
132
@@ -156,7 +156,7 @@ func jerry(_ i: Int)(j: Int) throws -> Int {
156
156
157
157
`throws ` is tracked as part of the type system: a function value must
158
158
also declare whether it can throw . Functions that cannot throw are a
159
- subtype of functions that can, so you can use a function that can\ 't
159
+ subtype of functions that can, so you can use a function that can't
160
160
throw anywhere you could use a function that can:
161
161
162
162
```swift
@@ -172,7 +172,7 @@ handle the error.
172
172
A call to a function which can throw within a context that is not
173
173
allowed to throw is rejected by the compiler.
174
174
175
- It isn\ 't possible to overload functions solely based on whether the
175
+ It isn't possible to overload functions solely based on whether the
176
176
functions throw . That is , this is not legal:
177
177
178
178
```swift
@@ -227,13 +227,15 @@ a direct call to a `rethrows` function is considered to not throw if it
227
227
is fully applied and none of the function arguments can throw . For
228
228
example:
229
229
230
- // This call to map is considered not to throw because its
231
- // argument function does not throw.
232
- let absolutePaths = paths.map { " /" + $0 }
230
+ ```swift
231
+ // This call to map is considered not to throw because its
232
+ // argument function does not throw.
233
+ let absolutePaths = paths.map { " /" + $0 }
233
234
234
- // This call to map is considered to throw because its
235
- // argument function does throw.
236
- let streams = try absolutePaths.map { try InputStream (filename : $0 ) }
235
+ // This call to map is considered to throw because its
236
+ // argument function does throw.
237
+ let streams = try absolutePaths.map { try InputStream (filename : $0 ) }
238
+ ```
237
239
238
240
For now, `rethrows ` is a property of declared functions, not of function
239
241
values. Binding a variable (even a constant) to a function loses the
@@ -345,7 +347,7 @@ scopes (that permit it), rather than relying on the programmer to
345
347
manually check for errors and do their own control flow. This is just a
346
348
lot less boilerplate for common error handling tasks. However, doing
347
349
this naively would introduce a lot of implicit control flow, which makes
348
- it difficult to reason about the function\ 's behavior. This is a serious
350
+ it difficult to reason about the function's behavior. This is a serious
349
351
maintenance problem and has traditionally been a considerable source of
350
352
bugs in languages that heavily use exceptions.
351
353
@@ -378,7 +380,7 @@ func readStuff() throws {
378
380
}
379
381
```
380
382
381
- Developers can choose to \ " scope\ " the `try` very tightly by writing it
383
+ Developers can choose to " scope" the `try ` very tightly by writing it
382
384
within parentheses or on a specific argument or list element:
383
385
384
386
```swift
@@ -395,7 +397,7 @@ let array = [ try foo(), bar(), baz() ]
395
397
396
398
Some developers may wish to do this to make the specific throwing calls
397
399
very clear. Other developers may be content with knowing that something
398
- within a statement can throw. The compiler\ ' s fixit hints will guide
400
+ within a statement can throw . The compiler's fixit hints will guide
399
401
developers towards inserting a single `try ` that covers the entire
400
402
statement. This could potentially be controlled someday by a coding
401
403
style flag passed to the compiler.
@@ -411,7 +413,7 @@ expression is considered to handle any error originating from within its
411
413
operand.
412
414
413
415
`try ! ` is otherwise exactly like `try `: it can appear in exactly the
414
- same positions and doesn\ ' t affect the type of an expression.
416
+ same positions and doesn't affect the type of an expression.
415
417
416
418
## Manual propagation and manipulation of errors
417
419
@@ -430,7 +432,7 @@ As such, the Swift standard library should provide a standard Rust-like
430
432
propagating the error in the current context.
431
433
432
434
This is something that composes on top of the basic model, but that has
433
- not been designed yet and details aren\ ' t included in this proposal.
435
+ not been designed yet and details aren't included in this proposal.
434
436
435
437
The name `Result< T> ` is a stand- in and needs to be designed and
436
438
reviewed, as well as the basic operations on the type.
@@ -488,7 +490,7 @@ based on real-world usage experience.
488
490
489
491
## Importing Cocoa
490
492
491
- If possible, Swift\ ' s error-handling model should transparently work
493
+ If possible, Swift's error-handling model should transparently work
492
494
with the SDK with a minimal amount of effort from framework owners.
493
495
494
496
We believe that we can cover the vast majority of Objective-C APIs with
@@ -521,7 +523,7 @@ couple of simple heuristics:
521
523
- Also common is a pointer result, where a `nil` result usually means
522
524
an error occurred. This appears to be universal in Objective-C; APIs
523
525
that can return `nil` results seem to do so via out-parameters. So
524
- it seems to be safe to make a policy decision that it\ ' s okay to
526
+ it seems to be safe to make a policy decision that it's okay to
525
527
assume that a `nil` result is an error by default.
526
528
527
529
If the pattern for a method is that a `nil` result means it produced
@@ -534,7 +536,7 @@ For other sentinel cases, we can consider adding a new clang attribute
534
536
to indicate to the compiler what the sentinel is:
535
537
536
538
- There are several APIs returning `NSInteger` or `NSUInteger`. At
537
- least some of these return 0 on error, but that doesn\ ' t seem like a
539
+ least some of these return 0 on error, but that doesn't seem like a
538
540
reasonable general assumption.
539
541
- `AVFoundation` provides a couple methods returning
540
542
`AVKeyValueStatus`. These produce an error if the API returned
@@ -564,16 +566,16 @@ just Swift. (For example, they could try to detect an invalid error
564
566
check.)
565
567
566
568
Cases that do not match the automatically imported patterns and that
567
- lack an attribute would be left unmodified (i.e., they\ ' d keep their
568
- NSErrorPointer argument) and considered \ " not awesome\ " in the SDK
569
+ lack an attribute would be left unmodified (i.e., they'd keep their
570
+ NSErrorPointer argument) and considered "not awesome" in the SDK
569
571
auditing tool. These will still be usable in Swift: callers will get the
570
572
NSError back like they do today, and have to throw the result manually.
571
573
572
574
For initializers, importing an initializer as throwing takes precedence
573
575
over importing it as failable. That is, an imported initializer with a
574
576
nullable result and an error parameter would be imported as throwing.
575
577
Throwing initializers have very similar constraints to failable
576
- initializers; in a way, it\ ' s just a new axis of failability.
578
+ initializers; in a way, it's just a new axis of failability.
577
579
578
580
One limitation of this approach is that we need to be able to
579
581
reconstruct the selector to use when an overload of a method is
@@ -601,38 +603,38 @@ APIs in the public API:
601
603
The above translation rule would import methods like this one from
602
604
`NSDocument`:
603
605
604
- ```objc
605
- - (NSDocument *)duplicateAndReturnError:(NSError **)outError;
606
- ```
606
+ ```objc
607
+ - (NSDocument *)duplicateAndReturnError:(NSError **)outError ;
608
+ ```
607
609
608
610
like so:
609
611
610
- ```swift
611
- func duplicateAndReturnError() throws -> NSDocument
612
- ```
612
+ ```swift
613
+ func duplicateAndReturnError() throws -> NSDocument
614
+ ```
613
615
614
616
The `AndReturnError` bit is common but far from universal; consider this
615
617
method from `NSManagedObject`:
616
618
617
- ```objc
618
- - (BOOL)validateForDelete:(NSError **)error;
619
- ```
619
+ ```objc
620
+ - (BOOL)validateForDelete:(NSError **)error ;
621
+ ```
620
622
621
623
This would be imported as:
622
624
623
- ```swift
624
- func validateForDelete() throws
625
- ```
625
+ ```swift
626
+ func validateForDelete() throws
627
+ ```
626
628
627
- This is a really nice import, and it\ ' s somewhat unfortunate that we
628
- can\ ' t import `duplicateAndReturnError:` as `duplicate()`.
629
+ This is a really nice import, and it's somewhat unfortunate that we
630
+ can't import `duplicateAndReturnError:` as `duplicate ()` .
629
631
630
632
## Potential future extensions to this model
631
633
632
634
We believe that the proposal above is sufficient to provide a huge step
633
635
forward in error handling in Swift programs, but there is always more to
634
- consider in the future. Some specific things we\ ' ve discussed (and may
635
- come back to in the future) but don\ ' t consider to be core to the Swift
636
+ consider in the future. Some specific things we've discussed (and may
637
+ come back to in the future) but don't consider to be core to the Swift
636
638
2.0 model are:
637
639
638
640
### Higher-order polymorphism
@@ -649,7 +651,7 @@ func map<T, U>(_ array: [T], fn: T -> U) throwsIf(fn) -> [U] {
649
651
}
650
652
```
651
653
652
- There\ ' s no need for a more complex logical operator than disjunction
654
+ There's no need for a more complex logical operator than disjunction
653
655
for normal higher- order stuff.
654
656
655
657
This feature is highly desired (e.g . it would allow many otherwise
@@ -682,30 +684,30 @@ autoreleasepool {
682
684
}
683
685
```
684
686
685
- The error-handling model doesn\ ' t cause major problems for this. The
687
+ The error- handling model doesn't cause major problems for this. The
686
688
compiler can infer that the closure throws , and `autoreleasepool` can be
687
689
overloaded on whether its argument closure throws ; the overload that
688
690
takes a throwing closures would itself throw .
689
691
690
692
There is one minor usability problem here, though. If the closure
691
693
contains throwing expressions, those expressions must be explicitly
692
- marked within the closure with `try`. However, from the compiler\ ' s
694
+ marked within the closure with `try `. However, from the compiler's
693
695
perspective, the call to `autoreleasepool` is also a call that can
694
696
throw , and so it must also be marked with `try `:
695
697
696
698
```swift
697
- try autoreleasepool { // 'try' is required here...
699
+ try autoreleasepool { // 'try' is required here...
698
700
let string = try parseString () // ...and here.
699
701
...
700
702
}
701
703
```
702
704
703
705
This marking feels redundant. We want functions like `autoreleasepool`
704
706
to feel like statements, but marks inside built- in statements like `if `
705
- don\ ' t require the outer statement to be marked. It would be better if
706
- the compiler didn\ ' t require the outer `try`.
707
+ don't require the outer statement to be marked. It would be better if
708
+ the compiler didn't require the outer `try `.
707
709
708
- On the other hand, the \ " statement-like\ " story already has a number of
710
+ On the other hand, the " statement-like" story already has a number of
709
711
other holes: for example, `break `, `continue `, and `return ` behave
710
712
differently in the argument closure than in statements. In the future,
711
713
we may consider fixing that; that fix will also need to address the
@@ -727,9 +729,9 @@ here: 1) the memory management rules for CFErrors are unclear and
727
729
potentially inconsistent. 2 ) we need to know when an error is raised.
728
730
729
731
In principle, we could import POSIX functions into Swift as throwing
730
- functions, filling in the error from `errno`. It\ ' s nearly impossible to
732
+ functions, filling in the error from `errno`. It's nearly impossible to
731
733
imagine doing this with an automatic import rule, however; much more
732
- likely, we\ ' d need to wrap them all in an overlay.
734
+ likely, we'd need to wrap them all in an overlay.
733
735
734
736
In both cases, it is possible to pull these into the Swift error
735
737
handling model, but because this is likely to require massive SDK
0 commit comments