Errors in Swift

In Swift, it’s idiomatic to communicate errors via throwing functions and do-catch blocks. This approach makes it easy to see which functions can throw errors and to handle them using language constructs specifically designed for error handling.

import Foundation

// By convention, throwing functions are marked with `throws`
func f(arg: Int) throws -> Int {
    if arg == 42 {
        // We use `throw` to raise an error
        throw NSError(domain: "com.example", code: 0, userInfo: [NSLocalizedDescriptionKey: "can't work with 42"])
    }
    
    // If no error is thrown, we simply return the value
    return arg + 3
}

// Enum for custom errors
enum TeaError: Error {
    case outOfTea
    case power
}

func makeTea(arg: Int) throws {
    if arg == 2 {
        throw TeaError.outOfTea
    } else if arg == 4 {
        // In Swift, we don't have a direct equivalent to error wrapping,
        // but we can create custom errors with associated values if needed
        throw TeaError.power
    }
}

func main() {
    for i in [7, 42] {
        do {
            let result = try f(arg: i)
            print("f worked: \(result)")
        } catch {
            print("f failed: \(error.localizedDescription)")
        }
    }

    for i in 0..<5 {
        do {
            try makeTea(arg: i)
            print("Tea is ready!")
        } catch TeaError.outOfTea {
            print("We should buy new tea!")
        } catch TeaError.power {
            print("Now it is dark.")
        } catch {
            print("unknown error: \(error)")
        }
    }
}

main()

In this Swift code:

  1. We use throws to mark functions that can raise errors.

  2. Instead of returning an error as a second return value, we use Swift’s throw keyword to raise errors.

  3. We create a custom TeaError enum to represent specific error cases, which is similar to sentinel errors in Go.

  4. Swift uses do-catch blocks for error handling, which is similar to try-catch in other languages.

  5. We can catch specific error types or use a general catch-all clause.

  6. Swift doesn’t have a direct equivalent to Go’s error wrapping with %w, but we can create custom error types with associated values if we need to include additional context.

  7. Instead of errors.Is, we use pattern matching in catch clauses to handle specific error types.

This Swift code maintains the same logic and structure as the original Go code, adapted to Swift’s error handling paradigm.