try? and try!
Sometimes the sender does not care why the parcel came back: it came back, so it did not work out. Then there is no need to read the stamp; you can treat it as if there were no parcel. And sometimes the sender is sure: the address is right, the parcel will arrive, and there is no need to stretch a net.
For these two cases, try has two variants: try? and try!. Both call a function with throws without a do block. Let's take the ParseError enum and the parse function and look at the first variant:
enum ParseError: Error {
case empty, badFormat
}
func parse(_ text: String) throws -> Int {
if text.isEmpty { throw ParseError.empty }
guard let n = Int(text) else {
throw ParseError.badFormat
}
return n
}
let weight = (try? parse("12")) ?? 0
let broken = (try? parse("12a")) ?? 0
print(weight, broken) // 12 0try?: an error becomes nil
Let's go through the last three lines:
try? parse("12")calls the function. There is no error, and the result is an optionalInt?with the number12inside.try? parse("12a")gets the errorbadFormatbut hands it on to nobody: the error disappears, and the result becomesnil.?? 0takes the number out of the optional or puts in0if it holdsnil.printprints12 0.
From the optionals theme you know the box that may be empty. try? turns a thrown error into such an empty box. So you work with the result of try? as with any optional: with ??, with if let n = try? parse(text) or with guard let.
The parentheses around try? parse("12") are required here: without them try? takes in the whole expression to its right, ?? 0 included.
Here the post office picture is off. A returned parcel can be put aside, and the stamp read later. After try? there is no way to learn the reason: nil does not hold it. So try? is written only when one thing matters: whether it worked or not.
try!: there can be no error
try! also does without do, but it does not make an optional: the result is an ordinary value.
let fixed = try! parse("5")
print(fixed * 3) // 15fixed gets the number 5 of type Int, and the program prints 15. You write it this way only when an error is impossible by the program's logic: the text "5" is written right in the code, and it holds a number.
And if an error does arrive, the program crashes:
let typed = try! parse("") // ✗
print(typed)Fatal error: 'try!' expression unexpectedly raised an error: main.ParseError.emptyThe message reads like this: the expression with try! unexpectedly threw an error. The error itself is named at the end: the type ParseError and the case empty. main before them is the name of the program that the swiftc command built. The print was never reached.
try! is like ! on an optional: both open the box without looking. It does not fit data from outside: text from a user, from a file or from the network.
Interviewers like to ask this: how do try, try? and try! differ? The answer:
trylets the error go on, tocatchor to whoever called the function, and the result stays an ordinary value;try?turns the error intoniland the result into an optional, and the reason for the error is lost;try!gives an ordinary value, and on an error it crashes the program.
Common mistake
A default value was added without parentheses:
let total = try? parse("x") ?? 0 // ✗warning: left side of nil coalescing operator '??' has non-optional type 'Int', so the right side is never usedThis is a warning, not a compile error: the program builds, but it does not work as intended. try? acts on the whole expression to its right, not on one call. Swift reads the line as try? (parse("x") ?? 0):
??gets an ordinaryIntfromparse, so the0is never needed;totalgets an optional, and on an error it holdsnil, not0.
The fix is parentheses around the call: (try? parse("x")) ?? 0.
Key points
try?turns an error intoniland the result into an optional; the reason for the error is lost.- A default value is added with
??, andtry?with its call goes in parentheses:(try? parse(text)) ?? 0. try!gives an ordinary value and crashes the program on an error:Fatal error: 'try!' expression unexpectedly raised an error.try!is written only when an error is impossible by the program's logic; data from outside getstrywithcatch, ortry?.