Error handling and exceptions in OOP
Essence
In classic OOP languages (Java, C#, C++) errors are exceptions: an object is thrown, the stack unwinds to the nearest catch, and any call may implicitly abort execution. Swift takes a different route: an error is a value, not an exception.
How it works in Swift
Erroris an empty protocol; errors are usually modelled as anenumwith associated values or astruct.- A function declares
throws; the call must be marked withtry. There are no hidden exit paths:tryis visible in the code. - Under the hood
throwsis an ordinary return through an extra register, with no stack unwinding and no exception tables: throwing is cheap. - Typed throws (Swift 6):
throws(PaymentError)puts the error type into the signature,catchbecomes exhaustive and there is no boxing intoany Error. Untypedthrowsis equivalent tothrows(any Error). Result<Success, Failure>stores the outcome "for later" (callbacks, Combine);try?turns an error intonil,try!into a crash;deferruns cleanup on every exit path.- An Objective-C
NSExceptioncannot be caught by Swift code: it is a bug, not an error; the same applies tofatalError/precondition.
import Foundation
enum PaymentError: Error {
case insufficientFunds(missing: Decimal)
case cardExpired
}
// Typed throws (Swift 6): the error type is part of the signature.
func charge(_ amount: Decimal, balance: Decimal) throws(PaymentError) {
guard balance >= amount else {
throw .insufficientFunds(missing: amount - balance)
}
}
do {
try charge(100, balance: 30)
} catch .insufficientFunds(let missing) {
print("need \(missing) more") // need 70 more
} catch {
print("other: \(error)")
}
// Error as a value: Result keeps success/failure for later.
let result = Result { try charge(10, balance: 50) }
if case .failure(let e) = result { print(e) }
let maybe: Void? = try? charge(1, balance: 0) // nil on failure
print(maybe == nil) // true
What the interviewer looks for
- Separating expected errors (network, validation:
throws/Result) from programmer errors (a broken invariant:precondition, crash). - Typed throws matter in library code and in embedded/performance-critical places; in app code
any Erroris usually enough. - Relation to async: with
async throwserrors propagate throughawaitjust as through a normal call; aTaskkeeps its error behind theResult-like.value.