try, try?, try! — and what is rethrows?
Predict first
What is the type of x?
func load() throws -> Int? { nil }
let x = try? load()
print(type(of: x))
- Optional<Int>
- Optional<Optional<Int>>
Mental model. try forwards the problem. try? shreds the report. try! bets the process on it.
try propagates the error to the caller. try? converts any error to nil and success to an optional — the error itself is discarded. try! crashes if an error is thrown.
rethrows means “this function throws only if a closure argument throws”. map, filter, and friends are rethrows, which is why you can call them without try when your closure does not throw.
Since Swift 5, try? flattens: it does not wrap an already-optional result again.
Common traps
- Using
try?when you need to distinguish failure from absence. You lose the error entirely — no logging, no retry decision. - “
rethrowsis justthrowswith a nicer name.” It is a stricter contract: the function body itself cannot throw; only a throwing argument can propagate. - “
try!is fine in tests.” Even there it hides which step failed.XCTUnwrap/tryis more useful.
They might follow up with
- Can a
rethrowsfunction call a throwing function directly? No — only through a throwing closure parameter. - What is the difference between
tryandtry await? None in error semantics;try awaitcombines both effects.
Related questions
- 1.8.1 throws vs Result vs optional — when do you use each?
- 1.8.3 What are typed throws (throws(MyError))? Why do they exist?
← throws vs Result vs optional — when do you use each? What are typed throws (throws(MyError))? Why do they exist? →