Is there sugar-syntax for `Result.failure` propagation?

Is there sugar-syntax for propagating Result.failures? I find myself spending a lot of time typing code like

switch foo {
case .success(_): ()
case .failure(let error): return error
}

Is there a more concise way of doing this, or am I just making a mistake using Result for general purpose error handling?

Can you elaborate on why you're not throwing errors? That is definitely what the language recommends, although you are certainly not the only person who prefers to use Result instead.

There isn't a concise way of doing that kind of return block that falls into the rest of the function. The closest would be to save a few characters with omething like if case .failure(let error) = foo { return .failure(error) }. Alternatively, you can use map on Result if you're willing to write the rest of the function as a map closure.

1 Like

Cool, I'll take a look at Swift exceptions then.

I normally avoid exceptions because I don't like how they work in other languages I use. I'll read the error handling chapter and see what I prefer.

Thanks

In Swift, errors are basically just sugar for returning a Result (not literally, but the effect is the same). For example, they don't do the scary thing that exceptions do where they will silently skip over stack frames by unwinding.

1 Like

To be clear, Swift errors are not exceptions, and the connection with what some other languages do can be confusing. Errors in Swift are a possible effect of functions and computed properties (marked with throws or rethrows), meaning that the compiler and runtime will treat those functions in a special way. You should probably always use throw/throws/try, and only use Result in very specific cases (for example, callbacks with input that can fail).

1 Like

Not sure if applicable, but maybe you can use .map to turn a Result<T, E> into a Result<U, E>?

foo.map { success in
   // transform success, and failures are just propagated
}

However, this strategy may not be useful for side-effects?

With this trivial boilerplate written once and out of the way
extension Result where Failure == Error {
    init(_ execute: () throws -> Success) {
        do {
            self = .success(try execute())
        } catch {
            self = .failure(error)
        }
    }
}

extension Result {
    func result() throws -> Success {
        switch self {
        case .success(let success): return success
        case .failure(let failure): throw failure
        }
    }
}

it is a one liner to convert between throwing functions and functions returning result:

// MARK: original function returning non negative integer on success and negative on failure
func foo() -> Int {
    true ? 123 : -1
}

// MARK: same converted to throwing function
func bar() throws -> Int {
    let result = foo()
    if result < 0 {
        throw NSError(domain: "xxx", code: result, userInfo: nil)
    }
    return result
}

// MARK: same converted to Result returning function
func baz() -> Result<Int, Error> {
    .init(bar)
}

// MARK: same converted to throwing function
func qux() throws -> Int {
    try baz().result()
}

Those are already in the standard library.

(I can't comprehend what's supposed to be going on in the original code and so don't have a recommendation.)

3 Likes

@olavfosse I just wrote a pitch very much related to this thread, please see here: