📖 Scenario: Imagine you are building a small app that processes tasks. Some tasks might fail and throw errors. You want to write a function that accepts another function as input and calls it. If the input function throws an error, your function should pass that error up to the caller.
🎯 Goal: You will create a rethrowing function in Swift that calls a throwing function passed as a parameter. This will help you understand how to handle errors in higher-order functions.
📋 What You'll Learn
Create a throwing function called taskThatMightFail that throws an error of type TaskError.
Create a rethrowing function called performTask that accepts a throwing function parameter and rethrows errors.
Call performTask with taskThatMightFail inside a do-catch block to handle errors.
Print "Task succeeded" if no error occurs, or print the error message if it fails.
💡 Why This Matters
🌍 Real World
Rethrowing functions are useful when you write utility functions that call other functions which might throw errors. This helps keep error handling clean and flexible.
💼 Career
Understanding rethrowing functions is important for Swift developers working on apps that handle errors gracefully, such as network requests, file operations, or user input validation.
Progress0 / 4 steps
1
Define the error type and a throwing function
Create an enum called TaskError that conforms to Error with a case failed. Then create a function called taskThatMightFail that throws TaskError.failed.
Swift
Hint
Define an enum for errors and a function that always throws that error.
2
Create a rethrowing function
Create a function called performTask that accepts a parameter task of type () throws -> Void. Mark performTask with rethrows and call task() inside it.
Swift
Hint
Use rethrows to let performTask throw only if task throws.
3
Call the rethrowing function inside a do-catch block
Write a do-catch block where you call performTask with taskThatMightFail as the argument. In the catch block, print "Caught error: \(error)".
Swift
Hint
Use do and catch to handle errors from performTask.
4
Modify the throwing function to sometimes succeed and print success message
Change taskThatMightFail to accept a Boolean parameter shouldFail. If shouldFail is true, throw TaskError.failed, otherwise do nothing. Then call performTask with a closure that calls taskThatMightFail(shouldFail: false). In the do block, print "Task succeeded".
Swift
Hint
Use a closure to call taskThatMightFail with shouldFail: false and print success if no error.
Practice
(1/5)
1. What does the rethrows keyword indicate in a Swift function?
easy
A. The function cannot throw any errors.
B. The function always throws an error regardless of the closure.
C. The function only throws an error if the closure it receives throws an error.
D. The function catches all errors internally and never propagates them.
Solution
Step 1: Understand the role of rethrows
The rethrows keyword means the function itself does not throw errors unless the closure it accepts throws.
Step 2: Compare options with this meaning
The function only throws an error if the closure it receives throws an error. correctly states this behavior. Options B, C, and D misunderstand the conditional throwing nature of rethrows.
Final Answer:
The function only throws an error if the closure it receives throws an error. -> Option C
Quick Check:
rethrows means conditional throwing = A [OK]
Hint: Remember: rethrows means throw only if closure throws [OK]
Common Mistakes:
Thinking rethrows means always throws
Confusing rethrows with throws
Assuming rethrows catches errors
2. Which of the following is the correct syntax to declare a function that rethrows errors from a closure parameter named operation?
easy
A. func perform(operation: () -> Void) rethrows {}
B. func perform(operation: () rethrows -> Void) throws {}
C. func perform(operation: () throws -> Void) throws {}
D. func perform(operation: () throws -> Void) rethrows {}
Solution
Step 1: Identify correct placement of throws and rethrows
The closure parameter must be declared with throws if it can throw, and the function must be marked rethrows to propagate errors conditionally.
Step 2: Check each option
func perform(operation: () throws -> Void) rethrows {} correctly declares the closure as () throws -> Void and the function as rethrows. func perform(operation: () rethrows -> Void) throws {} incorrectly uses rethrows on the closure. func perform(operation: () throws -> Void) throws {} marks the function as throws instead of rethrows. func perform(operation: () -> Void) rethrows {} declares a non-throwing closure but marks the function as rethrows, which is invalid.
A. The function cannot be marked rethrows if it catches errors internally.
B. The closure parameter must not be marked throws.
C. The catch block should rethrow the error to propagate it.
D. The call to run must be inside a do-catch block.
Solution
Step 1: Understand rethrows and error catching
A function marked rethrows must not catch errors from the closure; it only propagates them. Catching errors inside breaks this rule.
Step 2: Analyze the code's catch block
The run function catches errors from operation() internally, so it cannot be marked rethrows. This causes a compile error.
Final Answer:
The function cannot be marked rethrows if it catches errors internally. -> Option A
Quick Check:
rethrows disallows internal catch = D [OK]
Hint: rethrows functions must not catch errors inside [OK]
Common Mistakes:
Marking function rethrows but catching errors inside
Confusing rethrows with throws
Expecting rethrows to handle errors silently
5. You want to write a Swift function processAll that takes an array of closures, each possibly throwing an error, and calls them all. If any closure throws, processAll should rethrow that error. Which is the best way to declare processAll?
hard
A. func processAll(tasks: [() throws -> Void]) rethrows {
for task in tasks {
try task()
}
}
B. func processAll(tasks: [() throws -> Void]) throws {
for task in tasks {
try task()
}
}
C. func processAll(tasks: [() -> Void]) rethrows {
for task in tasks {
task()
}
}
D. func processAll(tasks: [() throws -> Void]) {
for task in tasks {
try? task()
}
}
Solution
Step 1: Understand rethrows limitation
The rethrows keyword only works if the function takes exactly one throwing closure parameter. Here, tasks is an array of closures, so rethrows cannot be used.
Step 2: Choose correct error propagation
Since multiple closures can throw, processAll must be marked throws to propagate errors. func processAll(tasks: [() throws -> Void]) throws {
for task in tasks {
try task()
}
} correctly uses throws. func processAll(tasks: [() throws -> Void]) rethrows {
for task in tasks {
try task()
}
} incorrectly uses rethrows. func processAll(tasks: [() -> Void]) rethrows {
for task in tasks {
task()
}
} uses non-throwing closures. func processAll(tasks: [() throws -> Void]) {
for task in tasks {
try? task()
}
} ignores errors with try?.
Final Answer:
func processAll(tasks: [() throws -> Void]) throws {
for task in tasks {
try task()
}
} -> Option B
Quick Check:
Multiple throwing closures require throws, not rethrows = C [OK]
Hint: Use throws when multiple throwing closures are parameters [OK]
Common Mistakes:
Using rethrows with multiple throwing closures
Ignoring errors with try?
Declaring non-throwing closures when errors can occur
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.