Как известно, поддержка опциональных типов в Swift реализована через тип enum под названием Optional. И когда мы пишем:
let a: Int? = 1
это синтаксический сахар, а полная запись:
let a: Optional<Int> = .some(1)
По идее, все это знают, а язык хорошо прячет от нас детали реализации и однажды в голову вполне может придти идея завести в свой программе вот такой тип данных:
enum Progress { case none case some }
Например, вам нужно отслеживать какой-то процесс. Вы инициализируете переменную значением .none, а затем, если есть какой-то прогресс, то значение меняется на .some. Лаконично и изящно:
import Foundation
enum Progress { case none case some(Float) }
class Processing { var loadingProgress: Progress? init(_ progress: Progress) { loadingProgress = progress } func start() { loadingProgress = .some(1) } func check() { if case .some(_) = loadingProgress { print("The loading process have some progress") } } }
let p = Processing(.none) p.start() p.check()
При запуске мы получим вполне ожидаемое сообщение:
The loading process have some progress
Отлично, все работает. Но внезапно вы замечаете странное поведение - некоторые условия работают неверно. В чем же дело? Давайте запустим без сразу проверку без вызова метода start():
let p = Processing(.none) p.check()
Упс. Мы снова получили
The loading process have some progress
Но ведь никакого прогресса не было. Давайте убедимся в этом и выведем значение переменной loadingProgress:
let p = Processing(.none) p.check() print("The loading process have (p.loadingProgress!) progress")
Результатом будут две строки:
The loading process have some progress
The loading process have none progress
Вы наверное уже догадались в чем тут дело, так как я начал именно с описания работы опциональных типов. А вот без подсказки возможно было бы не так очевидно. Конечно дело в том, что переменная loadingProgress является Optional и именно с этим типом сравнивается значение .some и условие выполняется. Если мы изменим в нашем типе Progress значение .some например на .done, то проблема исчезнет. Можно придраться, что код не вполне оптимальный, но его целью является демонстрация проблемы. В большом проекте что-то такое вполне может проскользнуть и доставить массу проблем.
Более того, если мы попробуем провернуть тот же фокус со значением .none, то у нас ничего не выйдет:
if case .none = loadingProgress { print("The loading process have no progress") }
Xcode выдаст предупреждение:
Assuming you mean 'Optional<Progress>.none'; did you mean 'Progress.none' instead?
В то время как .some выполниться без каких-либо предупреждений.
¯\_(ツ)_/¯