<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto">I don’t get why that type checks:<div><br></div><div> 1. rescue always throws inside sync, therefore</div><div> 2. _syncHelper inside sync always throws, therefore</div><div> 3. sync always throws, therefore</div><div> 4. Shouldn’t sync be declared throws not rethrows?<br><br><div id="AppleMailSignature">-- Howard. </div><div><br>On 1 Jan 2018, at 1:10 pm, Charles Srstka via swift-users <<a href="mailto:swift-users@swift.org">swift-users@swift.org</a>> wrote:<br><br></div><blockquote type="cite"><div><blockquote type="cite" class="">On Dec 31, 2017, at 1:35 AM, Nevin Brackett-Rozinsky via swift-users <<a href="mailto:swift-users@swift.org" class="">swift-users@swift.org</a>> wrote:<br class=""></blockquote><blockquote type="cite" class=""><br class="">So, I know this doesn’t actually address your issue, but I think it is important to clarify that “rethrows” does not guarantee anything about the *type* of error thrown by a function. What “rethrows” implies is that the function *will not throw* unless at least one of its arguments throws.<br class=""><br class="">In particular, if the argument throws, the outer function can throw *any error or none at all*. For example,<br class=""><br class="">enum CatError: Error { case hairball }<br class="">enum DogError: Error { case chasedSkunk }<br class=""><br class="">func foo(_ f: () throws -> Void) rethrows -> Void {<br class=""> do { try f() }<br class=""> catch { throw CatError.hairball }<br class="">}<br class=""><br class="">do {<br class=""> try foo{ throw DogError.chasedSkunk }<br class="">} catch {<br class=""> print(error) // hairball<br class="">}<br class=""><br class="">Inside foo’s catch block, it is legal to throw any error, or not throw an error at all. But *outside* that catch block foo cannot throw, which is causing you consternation.<br class=""><br class="">• • •<br class=""><br class="">I don’t have a good solution for you, but in attempting to find one I *did* uncover something which compiles that probably shouldn’t. It seems that a “rethrows” function is currently allowed to throw if a *local* function throws:<br class=""><br class="">func rethrowing(_ f: () throws -> Void) rethrows -> Void {<br class=""> func localThrowing() throws -> Void { throw CatError.hairball }<br class=""> return try localThrowing()<br class="">}<br class=""><br class="">do {<br class=""> try rethrowing{ throw DogError.chasedSkunk }<br class="">} catch {<br class=""> print(error) // hairball<br class="">}<br class=""><br class="">I wouldn’t count on this functionality as it is most likely a bug. Indeed, if we pass in a non-throwing argument then we get a runtime error:<br class=""><br class="">rethrowing{ return } // EXC_BAD_ACCESS (code=1, address=0x0)<br class=""><br class="">Although, if we change “localThrowing” to use do/catch on a call to “f” and throw only in the catch block, or even use your “var caught: Error?” trick, then it appears to work as intended with no problems at runtime.<br class=""><br class="">• • •<br class=""><br class="">In the unlikely scenario that the above local-function behavior is valid and intended, the following function will, technically speaking, let you work around the issue you’re having:<br class=""><br class="">func withPredicateErrors <Element, Return><br class=""> (_ predicate: (Element) throws -> Bool,<br class=""> do body: @escaping ((Element) -> Bool) -> Return<br class=""> ) rethrows -> Return<br class="">{<br class=""> func bodyWrapper(_ f: (Element) throws -> Bool) throws -> Return {<br class=""> var caught: Error?<br class=""> let value = body{ elem in<br class=""> do {<br class=""> return try f(elem)<br class=""> } catch {<br class=""> caught = error<br class=""> return true<br class=""> }<br class=""> }<br class=""> if let caught = caught { throw caught }<br class=""> return value<br class=""> }<br class=""><br class=""> return try bodyWrapper(predicate)<br class="">}<br class=""><br class="">It is not pretty, and it probably relies on a compiler bug, but at the present time, against all odds, it look like this operates as you intend.<br class=""><br class="">Nevin<br class=""></blockquote><br class="">That’s not the only exploit of that sort to exist in the frameworks, actually—and if you look through the source code to the standard library, you’ll see that the Swift standard library actually uses this kind of thing from time to time. For example, take DispatchQueue.sync(), which is declared as ‘rethrows’ despite wrapping a C function that Swift can’t reason anything about. I’d always wondered how that worked, so at some point I looked it up. Here’s how it's implemented:<br class=""><br class="">The public function we’re all used to, sync():<br class=""><br class=""><font face="Menlo" class="">public func sync<T>(execute work: () throws -> T) rethrows -> T {<br class=""> return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 })<br class="">}<br class=""></font><br class=""><div class="">This basically passes everything on to a private method, _syncHelper, which looks like this:</div><div class=""><br class=""><font face="Menlo" class="">private func _syncHelper<T>(<br class=""> fn: (() -> Void) -> Void,<br class=""> execute work: () throws -> T,<br class=""> rescue: ((Error) throws -> (T))) rethrows -> T<br class="">{<br class=""> var result: T?<br class=""> var error: Error?<br class=""> withoutActuallyEscaping(work) { _work in<br class=""> fn {<br class=""> do {<br class=""> result = try _work()<br class=""> } catch let e {<br class=""> error = e<br class=""> }<br class=""> }<br class=""> }<br class=""> if let e = error {<br class=""> return try rescue(e)<br class=""> } else {<br class=""> return result!<br class=""> }<br class="">}</font></div><div class=""><br class=""></div><div class="">So basically, since every error thrown inside _syncHelper() is rethrown from one of the closures passed into it, that satisfies ‘rethrows’, and it also satisfies ‘rethrows’ for sync(), since anything thrown by it is thrown by another method that’s also declared “rethrows”. Sneaky.</div><div class=""><br class=""></div><div class="">Anyway, if you really need to do something like this, I’d recommend doing it the way the Swift standard library does, because:</div><div class=""><br class=""></div><div class="">1. They can’t break that in the compiler without also breaking their own code, and:</div><div class=""><br class=""></div><div class="">2. If they *do* break it because they’ve introduced a new, proper way to do this using some kind of actuallyRethrows() function or something, you’ll want to switch to that anyway.</div><div class=""><br class=""></div><div class="">Charles</div><div class=""><br class=""></div></div></blockquote><blockquote type="cite"><div><span>_______________________________________________</span><br><span>swift-users mailing list</span><br><span><a href="mailto:swift-users@swift.org">swift-users@swift.org</a></span><br><span><a href="https://lists.swift.org/mailman/listinfo/swift-users">https://lists.swift.org/mailman/listinfo/swift-users</a></span><br></div></blockquote></div></body></html>