Значение или nil
Чему вы научитесь:
- объяснять, что такое опционал и почему
Int("42")даётInt?, а неInt; - извлекать значение из опционала через
if letиguard let; - подставлять значение по умолчанию через
??и читать значение через?.; - объяснять, чем опасно принудительное извлечение
!и когда оно допустимо.

Представьте, что вам вручили подарочную коробку. В ней может лежать подарок, а может не лежать ничего, и пока коробка закрыта, этого не узнать. Пользоваться подарком, не открыв коробку, нельзя: сначала её открывают и смотрят, что внутри.
Программе такие коробки достаются постоянно. Вспомните преобразование типов: Int(2.9) всегда даёт целое число 2. А что, если превращать в число текст?
В тексте "42" число записано, а в тексте "forty-two" его нет. Swift не угадывает и не останавливает программу. Он отвечает честно — коробкой, которая может оказаться пустой:
let first = Int("42")
let second = Int("forty-two")
print(type(of: first)) // Optional<Int>
print(first == nil, second == nil) // false trueДавайте разберём программу по строкам:
Int("42")находит в тексте число42и кладёт его в коробкуfirst.- В тексте
"forty-two"числа нет, иInt(…)возвращает пустую коробку. Пустоту в Swift записывают словомnil. print(type(of: first))печатает тип коробки —Optional<Int>, а неInt.- Сравнение с
nilотвечает на вопрос «коробка пуста?»:firstне пуста, аsecondпуста.
Тип Int?
Опционал хранит либо значение определённого типа, либо nil — «ничего». Тип опционала пишут как обычный тип со знаком вопроса: Int? читается как «Int или ничего». Полное имя того же типа — Optional<Int>, его и показал type(of:).
Знак вопроса ставят после любого типа: Double?, String?, Bool?. Форма у коробки-опционала одна: в Int? лежит либо целое число, либо ничего, но никогда не текст.
Откуда берутся опционалы
Результат, которого может не быть
Int(…) от текста — первый пример. Действие может не получиться, и тогда результата нет. Swift сообщает об этом через тип: раз результат — Int?, значит, его может и не быть. Читая код, вы сразу видите, где нужна проверка.
Значение, которого пока нет
Опционал объявляют и сами, когда значение появится позже. Например, лучший результат игрока неизвестен, пока не сыграна первая партия:
var bestScore: Int? = nil
print(bestScore == nil) // true
bestScore = 120
print(bestScore == nil) // falsevar bestScore: Int? = nilзаводит пустую коробку для целого числа. Тип здесь пишут обязательно: по одномуnilSwift не поймёт, какая коробка нужна.- Первая проверка печатает
true: коробка пуста. bestScore = 120кладёт в коробку число. Отдельно упаковывать его не нужно: это Swift сделает сам.- Вторая проверка печатает
false.
Обычный Int так не умеет. Строка let count: Int = nil не скомпилируется: 'nil' cannot initialize specified type 'Int'. В Int всегда лежит число, а пустым может быть только опционал.
Int? — не Int
Коробка с числом — ещё не число. Попробуем прибавить к first единицу:
print(first + 1) // ✗error: value of optional type 'Int?' must be unwrapped to a value of type 'Int'Компилятор отказывается собирать программу. Сообщение говорит: значение типа Int? нужно извлечь, чтобы получить Int. Извлечение — это и есть открытие коробки: проверить, есть ли в ней значение, и достать его. Пока коробка закрыта, складывать нечего: вдруг там nil?
Здесь картинка с коробкой неточна. Настоящую коробку можно потрясти и угадать, что внутри. Компилятор угадывать не разрешает: даже когда вы уверены, что first не пуста, значение используют только после извлечения.
Зачем нужны опционалы
Этот вопрос часто задают на собеседовании: «Что такое опционал и какую ошибку он предотвращает?» Ответ состоит из двух частей:
- опционал — тип вроде
Int?, в котором лежит либо значение, либоnil; - компилятор не даёт использовать
Int?какInt, пока значение не извлекли, поэтому забыть о пустом случае не получится.
Во многих языках пустым может оказаться почти любое значение. Об этом узнают только при запуске, когда программа аварийно завершается на строке, которая взяла пустое значение. В Swift пустым бывает только опционал, и это видно по его типу ещё до запуска.
Частая ошибка
Опционал напечатали, не извлекая значение:
print(first) // ✗warning: expression implicitly coerced from 'Int?' to 'Any'Это предупреждение, поэтому программа запустится. Но она напечатает Optional(42), а не 42: print показывает коробку вместе с содержимым, а пустую коробку печатает словом nil. Сначала извлеките значение, а печатайте уже его.
Главное
- Опционал
Int?хранит либо значение типаInt, либоnil— «ничего». Int("42")даётInt?: в тексте может не оказаться числа, и тогда результат —nil.Int?— неInt: пока значение не извлекли, компилятор не даст работать с ним как с числом.nilможет хранить только опционал, а если напечатать опционал без извлечения, компилятор выдаст предупреждение.