throws vs Result vs optional — when do you use each?

1.8.1 · L1 · Interview

Mental model. throws is a fire alarm. Result is a fire report you can file. An optional is “nobody was home”.

throws — typed failure at a function boundary. The caller must try, and the failure is handled at the point of the call. Best for user-initiated work where you can react immediately.

Result — a success/failure value you can store, pass, or deliver asynchronously (completion handlers, cached outcomes, Combine). It makes failure explicit in the type without using control flow.

Optional — absence is not an error. firstIndex(of:) returning nil means “not there”, not “something went wrong”. Do not wrap every miss in an Error.

throwsResultOptional
Says why it failedyesyesno
Call sitetryswitch / get()if let / ??
Typed errorwith throws(E)yesno
Store or pass aroundnoyesyes
Best formost APIsstored outcomesabsence is normal

Common traps

  • Using Result everywhere because it is “more functional”. It adds unwrapping noise to synchronous code that could just throw.
  • Throwing an error for “not found”. If absence is normal, an optional is the honest type.
  • Converting throws to Result inside the callee. Let callers choose; Result(catching:) exists for that.

They might follow up with

  • What does Result(catching:) do? Runs a throwing closure and packages the outcome as .success or .failure.
  • What about async? async throws is the default; Result is still useful for stored or streamed outcomes.

Related questions

  • 1.8.2 try, try?, try! — and what is rethrows?
  • 1.8.3 What are typed throws (throws(MyError))? Why do they exist?

try, try?, try! — and what is rethrows? →