Skip to content
Theory · Errors · lesson 4 of 7

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:

SWIFT
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 0

try?: an error becomes nil

Let's go through the last three lines:

  1. try? parse("12") calls the function. There is no error, and the result is an optional Int? with the number 12 inside.
  2. try? parse("12a") gets the error badFormat but hands it on to nobody: the error disappears, and the result becomes nil.
  3. ?? 0 takes the number out of the optional or puts in 0 if it holds nil.
  4. print prints 12 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.

SWIFT
let fixed = try! parse("5")
print(fixed * 3) // 15

fixed 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:

SWIFT
let typed = try! parse("") // ✗
print(typed)
TEXT
Fatal error: 'try!' expression unexpectedly raised an error: main.ParseError.empty

The 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:

  • try lets the error go on, to catch or to whoever called the function, and the result stays an ordinary value;
  • try? turns the error into nil and 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:

SWIFT
let total = try? parse("x") ?? 0 // ✗
TEXT
warning: left side of nil coalescing operator '??' has non-optional type 'Int', so the right side is never used

This 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 ordinary Int from parse, so the 0 is never needed;
  • total gets an optional, and on an error it holds nil, not 0.

The fix is parentheses around the call: (try? parse("x")) ?? 0.

Key points

  • try? turns an error into nil and the result into an optional; the reason for the error is lost.
  • A default value is added with ??, and try? 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 gets try with catch, or try?.