Write a higher-order function that throws only when its closure throws
Write a generic function firstMatch that returns the first array element satisfying a predicate closure, or nil when none matches.
Requirements:
- The predicate may itself throw;
firstMatchmust throw only when the predicate throws. - A caller passing a non-throwing predicate must be able to call
firstMatchwithouttry. - Do not add any error handling of your own inside the function.
func firstMatch<T>(in items: [T], where predicate: (T) throws -> Bool) rethrows -> T? {
// your code here
}
Write the implementation.
rethrows marks a function that throws only when a closure it calls throws. With a non-throwing closure the caller invokes it without try; with a throwing one the call becomes throwing. Plain throws would force try on every caller.
- ✗Thinking
rethrowscatches or swallows the closure's error - ✗Believing a
rethrowsfunction forcestryeven with a non-throwing closure - ✗Confusing
rethrowswith retrying the operation on failure
- →Why can a
rethrowsfunction not throw an error of its own? - →How does the caller's
tryrequirement change with the closure passed in?
rethrows lets a function forward its closure's error without declaring one of its own:
func firstMatch<T>(in items: [T], where predicate: (T) throws -> Bool) rethrows -> T? {
for item in items {
if try predicate(item) { // try is required here, inside the body
return item
}
}
return nil
}
// non-throwing predicate — call without try
let n = firstMatch(in: [1, 2, 3]) { $0 > 1 }
// throwing predicate — call requires try
let m = try firstMatch(in: users) { try validate($0) }
Inside the body try predicate(item) is mandatory. Outside, the try requirement depends on the closure passed: a non-throwing one lets the caller omit try; a throwing one makes the call throwing. Using throws instead of rethrows would force even the first caller to write try/catch when no error is possible. This is exactly why map, filter, and first(where:) are declared rethrows in the standard library.