If different failures require different actions, preserve them with throws or Result instead of erasing them with try?. throws propagates errors through a call; Result stores success or failure as a value for later handling.
Mechanism and design choice
ParseError distinguishes nonnumeric input from a negative number. throws(ParseError) specifies the thrown error type, allowing do-catch to select an appropriate response. try? collapses both failures into nil, which is insufficient when recovery policies differ. Primary source
Reproduce the experiment
Pass cat, -1, 0, and 42 through the same parser. Verify that try? yields nil for both failures, then store each outcome as Result<Int, ParseError>. A typed do block makes the caught error type explicit; get retrieves stored successes or rethrows failures.
This standalone example was run on 2026-10-23 with Apple Swift 6.4 targeting arm64 macOS, in Swift 6 language mode. It needs no network or additional packages.
Download the complete Swift file
Run the command in the download directory. Preconditions stop execution if a check fails. Do not use -Ounchecked: it removes these checks.
swift -swift-version 6 swift-error-handling-throws-result.swift
enum ParseError: Error, Equatable { case notInteger, negative }
func parse(_ raw: String) throws(ParseError) -> Int {
guard let value = Int(raw) else { throw .notInteger }
guard value >= 0 else { throw .negative }
return value
}
precondition((try? parse("cat")) == nil && (try? parse("-1")) == nil)
let results: [Result<Int, ParseError>] = ["cat", "-1", "0", "42"].map { raw in
do throws(ParseError) { return .success(try parse(raw)) }
catch { return .failure(error) }
}
precondition(results == [.failure(.notInteger), .failure(.negative), .success(0), .success(42)])
do {
_ = try results[1].get()
preconditionFailure("failure was lost")
} catch ParseError.negative {}
let recovered = try results[3].get()
precondition(recovered == 42)
print("try?-failures=nil,nil result=notInteger,negative,0,42 get-error=negative")
Recorded output and interpretation
try?-failures=nil,nil result=notInteger,negative,0,42 get-error=negative
The array retains notInteger, negative, 0, and 42 separately. Calling get on the failed result throws negative again; the successful result returns 42. No network or file I/O is involved in this check of error-information preservation.
Scope and limitations
throws cannot catch every program failure. Traps from force unwrapping or failed preconditions are outside this contract. A failing try! is not a normal recovery path either. This experiment only runs synchronous parsing, not asynchronous cancellation, retries, or file failures.
What to check next
Define error cases around actions the caller can distinguish. Use Optional for simple absence, throws to convey reasons, and Result to store or aggregate outcomes. Attach UI messaging and logging at a boundary that preserves the actual cause.
Sources and verification
- The Swift Programming Language: Error HandlingSwift.org · Official documentation · Checked October 23, 2026Supports: Source for the implementation contract and explanation. The output was checked separately using the experiment included here.

