Skip to content

Instantly share code, notes, and snippets.

@manmal
Created December 15, 2025 09:01
Show Gist options
  • Select an option

  • Save manmal/f5edd30a7cedea8db9c4b39e765a0df9 to your computer and use it in GitHub Desktop.

Select an option

Save manmal/f5edd30a7cedea8db9c4b39e765a0df9 to your computer and use it in GitHub Desktop.

SE-0413: Typed Throws

1. Core Semantics

The throws(T) syntax restricts a function to throwing a specific concrete error type T, which must conform to the Error protocol. The legacy throws keyword is preserved as syntactic sugar equivalent to throws(any Error), maintaining backward compatibility while allowing for precise error contracts.

2. Never & Uniformity

A function defined as throws(Never) is semantically identical to a non-throwing function, allowing the compiler to optimize away error handling paths. This unifies the type system, enabling generic algorithms to accept both throwing and non-throwing closures without requiring separate overloads.

3. Subtyping (Covariance)

Function types exhibit covariance in their error type, meaning a function throwing a specific error (or Never) can be passed where a function throwing a more general error (like any Error) is expected. This hierarchy establishes that throws(Never) is a subtype of throws(Concrete), which in turn is a subtype of throws(any Error).

4. Inference (Closures)

The compiler analyzes the body of a closure to automatically determine its thrown error type based on the specific error values thrown. If all throw statements yield the same type E, the closure is inferred as throws(E); otherwise, it defaults to the type-erased throws(any Error).

5. Do / Catch

When a do block contains calls throwing a single specific error type, the associated catch block receives an implicitly typed error constant of that concrete type without needing casting. Developers can also explicitly annotate do throws(E) to enforce at compile-time that the enclosed code only throws type E.

6. Generics vs Rethrows

Generic functions can now specify a type parameter E: Error for their throwing behavior, allowing the output error type to strictly match the input closure's error type. This mechanism effectively supersedes rethrows by preserving exact type information, whether the caller passes a function throwing Never, MyError, or any Error.

7. Protocols & Result

Protocol requirements can dictate a specific error type, compelling conforming types to throw exactly that type or a subtype (like Never). The standard library's Result type gains a new initializer that preserves the error type of the throwing closure passed to it, rather than erasing it to any Error.

8. Limitations

Swift does not support structural union types for errors (e.g., throws(NetworkError | DatabaseError) is invalid), requiring distinct errors to be wrapped in a container enum. If a function can originate multiple distinct error types that are not wrapped, they must be type-erased to the common supertype any Error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment