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.
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.
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).
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()
}