Errors, we’ve all got ‘em. What do we do with ‘em?

Swift has a couple of different error handling patterns. There’s try and catch which I always think of as kind of the nuclear option. Something went wrong, the code blows up with a throw and our hero, the intrepid programmer, has to deal with it right here, right now. And then there’s Result, an elegant weapon for a more civilized age. Call a method and get back a Result, then deal with it as you like – check for the value, handle the error or maybe even ignore it altogether. Depending on your situation, one of these is likely to suit better than the other.

But sometimes the situation changes. Maybe you wrote some code that should obviously throw(or calls some library code that throws), but it would really make a lot more sense to return a Result. As it turns out, it’s really easy to convert between try/catch and a Result type.

Imagine you have some code that can throw. Maybe even it always throws, because it is a badIdea(). But you need to have a Result instance instead. The Result type has an initializer that handles this perfectly. You hand it a closure that returns the Success type for the Result and any error thrown by the closure is used for the Failure case. Basically, they’ve wrapped up all the boilerplate in this initializer, and you get a very concise way to convert try into a Result.

enum HorrendousError: Error {
    case veryBad
}

func badIdea() throws -> String {
    throw HorrendousError.veryBad
}

let result = Result<String, Error> { try badIdea() }

The full signature of that initializer is Result.init(catching: () -> Success). The single argument is a closure returns the Success type – here that’s a String. If the closure throws, then that gets wrapped up as the .failure case.

How about doing the reverse? You’ve got a result type, but you’d rather access the value in a try / catch sort of way. The Result type itself has a accessor method for just such an occasion!

let value = try someResult.get()

If the Result type is the .success case, it just returns the successful value. If someResult is a .failure case, the get() method will throw. This is especially useful if you want to access the result and ignore any errors:

let optionalValue = try? someResult.get()

This is also handy in testing when you expect a Result type to be successful. Use the get() method to access the value and let XCTest’s support for tests that throw to handle the error cases. This can make tests shorter and easier to read too.

For me, discovering the interchangeable nature of try/catch and the Result type was a bit of an ‘aha!’ moment. You’re not locked in to on type of error handling or another. With these convenient converters, you can fluidly move from one type to another as needed. And without a lot of boilerplate code for the conversions!