What are typed throws (throws(MyError))? Why do they exist?

1.8.3 · L3 · Interview

Predict first

What does this print?

enum ParseError: Error { case empty }

func parse(_ s: String) throws(ParseError) -> Int {
    if s.isEmpty { throw .empty }
    return s.count
}

do { _ = try parse("") }
catch { print(type(of: error)) }
  • Error
  • any Error
  • ParseError

Mental model. Untyped throws says “something may go wrong”. Typed throws says exactly which things.

Swift 6 makes the thrown type part of the function type: func load() throws(ParseError) -> Data. Callers can catch exhaustively over an enum, and generics can be written over the error type.

It also avoids boxing to any Error, which helps in constrained environments (embedded, very hot paths). Interop with untyped throws still requires boxing at the boundary.

With typed throws the catch knows the exact error type, so error is a ParseError, not any Error.

Common traps

  • “Typed throws replaces Result.” No — it is still control flow. Result is still the right choice for stored or asynchronous outcomes.
  • “I should type every function now.” It is a compatibility commitment: every new error case is a source break for exhaustive catch sites.

They might follow up with

  • Can a typed-throws function call an untyped one? Yes, but the error is converted to the declared type or boxed — the compiler enforces the declared type.
  • What is the ABI story? Typed throws is part of the function type, so changing it is a source and potentially ABI break.

Related questions

  • 1.8.1 throws vs Result vs optional — when do you use each?
  • 1.8.2 try, try?, try! — and what is rethrows?

← try, try?, try! — and what is rethrows?   What does defer guarantee? What is the order of multiple defers? →