ProgramGuru

Throw and handle recoverable errors

Throw and handle recoverable errors

A score form can receive a number outside the accepted range. Recoverable failure should be expressed in the function’s contract so callers decide how to respond. A throwing function can produce a value or throw an error; do-catch handles that error at the call site.

What you’ll learn

  • Throw a domain error and match it in catch.
  • Distinguish try, try? and an unsafe forced-success assertion.

Throw and handle recoverable errors in the example

  1. Validate range

    A score form can receive a number outside the accepted range.

    Validate range
  2. Throw on failure

    An error type conforms to Error.

    Throw on failure
  3. Catch and explain

    Validation throws before returning a mark.

    Catch and explain
Validation throws before returning a mark. Control leaves the do body and enters the specific catch, so no numeric score is printed. The final catch handles errors not covered by the earlier pattern.

How throw and handle recoverable errors works

An error type conforms to Error. Calling a throwing operation normally needs try, making possible failure visible. A catch block can match a specific case. try? converts failure into nil, while try! asserts no error will occur and can trap if that assertion is wrong.

Work through throw and handle recoverable errors

Save this complete program as main.swift and run it with the Swift toolchain. No separate input file is needed.

Swift · complete program

enum MarkError: Error { case outOfRange }
func validate(_ score: Int) throws -> Int {
  guard (0...100).contains(score) else { throw MarkError.outOfRange }
  return score
}
do {
  print(try validate(120))
} catch MarkError.outOfRange {
  print("Score must be 0...100")
} catch {
  print("Unexpected error")
}

Expected output

Score must be 0...100

Follow the execution

Validation throws before returning a mark. Control leaves the do body and enters the specific catch, so no numeric score is printed. The final catch handles errors not covered by the earlier pattern.

Try this program in the Swift compiler. Paste the complete source above.

Practice: Throw and handle recoverable errors

Your task: What happens if validate receives 80 instead?

Swift · complete program

enum MarkError: Error { case outOfRange }
func validate(_ score: Int) throws -> Int {
  guard (0...100).contains(score) else { throw MarkError.outOfRange }
  return score
}
do {
  print(try validate(120))
} catch MarkError.outOfRange {
  print("Score must be 0...100")
} catch {
  print("Unexpected error")
}

Worked answer

It returns 80 and the print displays that value. Neither catch executes because no error was thrown.

Handle failure without forcing success

Defer schedules cleanup when the enclosing scope exits under normal language execution, including a throw. It does not make every failure recoverable or catch arbitrary process crashes. Use errors for expected failure paths with a response policy.

Check your decision

What happens if validate receives 80 instead?
Choose one answer. You can try again.

Answer and explanation

It returns 80 and the print displays that value.

It returns 80 and the print displays that value. Neither catch executes because no error was thrown.

Handle failure without forcing success

It returns 80 and the print displays that value. Neither catch executes because no error was thrown. Defer schedules cleanup when the enclosing scope exits under normal language execution, including a throw.

Further reading: official Swift language guide.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Read input and work with files →