|
| 1 | +# Add Generic Result and Error Handling to autoreleasepool() |
| 2 | + |
| 3 | +* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-autoreleasepool-signature.md) |
| 4 | +* Author(s): [Timothy J. Wood](https://github.com/tjw) |
| 5 | +* Status: **Awaiting review** |
| 6 | +* Review manager: TBD |
| 7 | + |
| 8 | +## Introduction |
| 9 | + |
| 10 | +The `autoreleasepool` function in the standard library does not currently |
| 11 | +support a return value or error handling, making it difficult and error-prone |
| 12 | +to pass results or errors from the body to the calling context. |
| 13 | + |
| 14 | +Swift-evolution thread: A first call for discussion was |
| 15 | +[made here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160314/013054.html). |
| 16 | +Dmitri Gribenko pointed out that adding a generic return type would be useful |
| 17 | +(first in my premature pull request) and then also [here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160321/013059.html). |
| 18 | +Jordan Rose [pointed out](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160321/013077.html) |
| 19 | +that care was needed to avoid inferring an incorrect return type for the body |
| 20 | +block, but after testing we confirmed that this is handled correctly by |
| 21 | +the compiler. |
| 22 | + |
| 23 | +## Motivation |
| 24 | + |
| 25 | +The current signature for `autoreleasepool` forces the creation of temporary |
| 26 | +variables to capture any results of the inner computation, as well as any error |
| 27 | +to eventually throw, in the case they are needed in the calling code. This extra |
| 28 | +boilerplate clutters up the intent, as well as introduces the risk of |
| 29 | +accidentally unwrapping a `nil` value. |
| 30 | + |
| 31 | +For example: |
| 32 | + |
| 33 | +```swift |
| 34 | +func doWork() throws -> Result { |
| 35 | + var result: Result? = nil |
| 36 | + var error: ErrorProtocol? = nil |
| 37 | + autoreleasepool { |
| 38 | + do { |
| 39 | + ... actual computation which hopefully assigns to result but might not ... |
| 40 | + } catch let e { |
| 41 | + error = e |
| 42 | + } |
| 43 | + } |
| 44 | + guard let result = result else { |
| 45 | + throw error! |
| 46 | + } |
| 47 | + return result! |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +## Proposed solution |
| 52 | + |
| 53 | +I'd like to propose altering the signature of the standard library |
| 54 | +`autoreleasepool` function to allow for a generic return type, as well as |
| 55 | +allowing a `throw` of an error: |
| 56 | + |
| 57 | +```swift |
| 58 | +public func autoreleasepool<Result>(@noescape body: () throws -> Result) rethrows -> Result |
| 59 | +``` |
| 60 | + |
| 61 | +The case above becomes much more clear and less error-prone since the compiler |
| 62 | +can enforce that exactly one of the error and result are used: |
| 63 | + |
| 64 | +```swift |
| 65 | +func doWork() throws -> Result { |
| 66 | + return try autoreleasepool { |
| 67 | + ... actual computation which either returns or throws ... |
| 68 | + } |
| 69 | +} |
| 70 | +``` |
| 71 | + |
| 72 | +As an aside, since this proposes changing the signature already, I would like |
| 73 | +to further propose changing the argument label from `code` to `body`. This seems |
| 74 | +more in line with the parameter name used in the rest of the standard library, |
| 75 | +but isn't central to this proposal. |
| 76 | + |
| 77 | +## Detailed design |
| 78 | + |
| 79 | +The updated standard library function would read: |
| 80 | + |
| 81 | +```swift |
| 82 | +public func autoreleasepool<Result>(@noescape body: () throws -> Result) rethrows -> Result { |
| 83 | + let pool = __pushAutoreleasePool() |
| 84 | + defer { |
| 85 | + __popAutoreleasePool(pool) |
| 86 | + } |
| 87 | + return try body() |
| 88 | +} |
| 89 | +``` |
| 90 | + |
| 91 | +## Impact on existing code |
| 92 | + |
| 93 | +No impact expected. |
| 94 | + |
| 95 | +## Alternatives considered |
| 96 | + |
| 97 | +The [original request, SR-842](https://bugs.swift.org/browse/SR-842) only |
| 98 | +suggested adding `throws`, but Dmitri Gribenko pointed out that adding a generic |
| 99 | +return type would be better. |
| 100 | + |
| 101 | +I also explored whether third-party code could wrap `autoreleasepool` themselves |
| 102 | +with something like: |
| 103 | + |
| 104 | +```swift |
| 105 | +func autoreleasepool_generic<ResultType>(@noescape code: Void throws -> ResultType) rethrows -> ResultType { |
| 106 | + var result:ResultType? |
| 107 | + var error:ErrorProtocol? |
| 108 | + |
| 109 | + autoreleasepool { |
| 110 | + do { |
| 111 | + result = try code() |
| 112 | + } catch let e { |
| 113 | + error = e |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + if let result = result { |
| 118 | + return result |
| 119 | + } |
| 120 | + |
| 121 | + throw error! // Doesn't compile. |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +but this doesn't compile, since in a function with `rethrows`, only the call to |
| 126 | +the passed in function that is marked as `throws` is allowed to throw. |
| 127 | +Even if it was possible to create a `rethrows` wrapper from the non-throwing |
| 128 | +function, it is better to add the safety to the standard library in the |
| 129 | +first place. |
0 commit comments