Result types

How do I use result types? When using a Result enum for callbacks, how to access the Error?

The most common Result enumeration looks like this:

enum Result<Value> { 
    case success(Value), failure(Error) }

A Result is used almost exclusively in completion handlers. In synchronous code, it’s more common to use throwing functions rather than Result types:

do {
    let value = try throwingFunction(...)
} catch {
    ... handle error ...
}

A Result type replaces the (value: Value?, error: Error?) -> Void handler signature used by many Cocoa APIs with this single Swift enumeration. Handling this type requires a slightly different approach than you’d use with thrown error handling.

As a rule, if an error is generated on your behalf, pay attention to it and don’t discard it out of hand. Errors help identify underlying issues that you may be able to resolve. They also provide important information for the developer and end-user of why an operation has failed.

The switch statement provides the simplest approach to handle both result conditions with equal priority:

switch result {
case .failure(let error): 
    // handle error here
case .success(let value): 
    // handle success here
}

If the error handling code is significantly less detailed than the success code, you might choose to perform early exit instead of using switch. This approach allows you to handle any errors and then move on to processing the returned value at the top scope.

Use an if statement (not a guard statement) to bind error instances. Its primary clause should handle the bound error  and then leave scope.  If the result is success, the if-test will fail. Follow the error check with a guard statement to bind the success value.

if case .failure(let error) = result {
    // handle error
    return
}
guard case .success(let value) = result 
    else { fatalError("Success value could not be bound") } // this should never happen

// use value

This second approach allows you to promote the typically detailed steps involved in processing a value after extracting it from the Result enumeration. The guard‘s else clause is a little ugly but necessary. Swift doesn’t offer a “forced enumeration unwrap” similar to Optional‘s !.

Breaking the handling down into an if/guard pair is not as elegant as the unified switch statement, but it provides a practical way to promote the importance of the returned value.

Update: If the !! operator is ever adopted, you could extend Result to return a computed var value: Value? member, and then use !! instead of the guard/fatalError combo in the above example to create a streamlined early return / value handling approach:

if case .failure(let error) = result { ... }
let value = result.value !! "Success value could not be bound"

It’s a lot cleaner. See this PR for more details. (Thanks Dave)

4 Comments

  • I would almost always prefer:

    guard case .success(let value) = result else {
         // handle error
         return
    }
    // use value
    

    Most error handling will either propagate the error somewhere or call specific error-handling routines, and in both of those cases it usually makes sense to pass things as the original result (of type Result but known to be failures) rather than doing the enumeration unwrap for failure cases all the time.

    I.e. put the unwrapping of the failure case inside the “// handle error” if you have to, but often you don’t, or at least can funnel into much fewer places where you have to.

  • That would look like this:

    guard case .success(let value) = result else {
         guard case .failure(let error) = error else { fatalError("bad stuff") }
         handle error
         return
    }
    // use value
    

    Which is also kind of icky…

    • Sure, in those spots it’s icky. But most of the time it’ll be

      // handle error:
      return errorHandler(result)

  • I add a throwing ‘value()’ method to Result, so that the unpacking of the value can be performed in a do-catch:
    { result in
    do {
    let a = try result.value()
    …use a…
    } catch {
    …handle error…
    }
    }

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>