[swift-users] Rethrows issue

Nevin Brackett-Rozinsky nevin.brackettrozinsky at gmail.com
Sun Dec 31 01:35:41 CST 2017


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.

In particular, if the argument throws, the outer function can throw *any
error or none at all*. For example,

enum CatError: Error { case hairball }
enum DogError: Error { case chasedSkunk }

func foo(_ f: () throws -> Void) rethrows -> Void {
    do    { try f() }
    catch { throw CatError.hairball }
}

do {
    try foo{ throw DogError.chasedSkunk }
} catch {
    print(error)    // hairball
}

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.

• • •

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:

func rethrowing(_ f: () throws -> Void) rethrows -> Void {
    func localThrowing() throws -> Void { throw CatError.hairball }
    return try localThrowing()
}

do {
    try rethrowing{ throw DogError.chasedSkunk }
} catch {
    print(error)    // hairball
}

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:

rethrowing{ return }  // EXC_BAD_ACCESS (code=1, address=0x0)

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.

• • •

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:

func withPredicateErrors <Element, Return>
    (_ predicate: (Element) throws -> Bool,
     do body: @escaping ((Element) -> Bool) -> Return
    ) rethrows -> Return
{
    func bodyWrapper(_ f: (Element) throws -> Bool) throws -> Return {
        var caught: Error?
        let value = body{ elem in
            do {
                return try f(elem)
            } catch {
                caught = error
                return true
            }
        }
        if let caught = caught { throw caught }
        return value
    }

    return try bodyWrapper(predicate)
}

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.

Nevin



On Sat, Dec 30, 2017 at 11:15 PM, Brent Royal-Gordon via swift-users <
swift-users at swift.org> wrote:

> I need to do something like this:
>
>         func withPredicateErrors<Element, Return>(_ predicate: (Element)
> throws -> Bool, do body: ((Element) -> Bool) -> Return) rethrows -> Return {
>           var caught: Error?
>           let value = body { elem in
>             do {
>               return try predicate(elem)
>             }
>             catch {
>               caught = error
>               return true     // Terminate search
>             }
>           }
>
>           if let caught = caught {
>             throw caught
>           }
>           else {
>             return value
>           }
>         }
>
> The problem is, the Swift compiler doesn't allow the explicit `throw`
> statement; even though it can only throw errors originally thrown by
> `predicate`, the compiler is not smart enough to prove that to itself. I
> cannot make `body` a `throws` function.
>
> Is there any way to do this? Either to override the compiler's safety
> check, or to rewrite this function to avoid it?
>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20171231/c6bb10cd/attachment.html>


More information about the swift-users mailing list