Определите вывод программы и объясните, что происходит при создании перечисления из неизвестного исходного значения:
enum HTTPStatus: Int {
case ok = 200
case notFound = 404
}
let success = HTTPStatus(rawValue: 200)
let missing = HTTPStatus(rawValue: 201)
print(success == HTTPStatus.ok, missing == nil)
Программа напечатает true true. Синтезированный инициализатор HTTPStatus(rawValue:) является failable initializer: он возвращает соответствующий случай, если исходное значение совпадает с rawValue, и nil, если такого случая нет.
Перечисления с исходными значениями нужны для представления фиксированного набора вариантов, который связан с внешним форматом: HTTP-кодами, кодами базы данных, строковыми ключами конфигурации или значениями протокола.
Swift отделяет внутренне безопасное представление случая перечисления от внешнего необработанного значения. Поэтому преобразование из внешнего значения не предполагается успешным автоматически: неизвестный код должен быть обработан явно.
Внешние данные могут содержать значение, которого нет среди объявленных случаев. Если считать любое число корректным HTTPStatus, приложение может неправильно интерпретировать ответ сервера или silently выбрать неподходящее поведение.
В приведённом коде 200 соответствует HTTPStatus.ok, поэтому success имеет тип HTTPStatus? и содержит .ok. Значение 201 не соответствует ни одному случаю, поэтому missing содержит nil.
При объявлении enum HTTPStatus: Int Swift делает перечисление соответствующим протоколу RawRepresentable. У каждого случая появляется доступное только для чтения свойство rawValue, а также инициализатор init?(rawValue:).
Инициализатор возвращает Optional, потому что преобразование может завершиться неуспешно. Успешный результат — конкретный случай перечисления, неуспешный — nil; исключение при этом не выбрасывается.
Обратное преобразование всегда определено для существующего случая: HTTPStatus.notFound.rawValue даст 404. Однако это не означает, что любое значение типа Int можно преобразовать в HTTPStatus.
Практическое следствие: результат init(rawValue:) нужно извлекать через if let, guard let, оператор ?? или другую явную обработку Optional. Принудительное извлечение через ! допустимо только при доказанной гарантии корректности входных данных, иначе неизвестное значение приведёт к аварийному завершению.
Клиент получает числовой код статуса из JSON. Вариант с ручным switch позволяет централизованно обработать каждый код, но дублирует таблицу соответствий и требует обновления при изменении перечисления. Вариант с HTTPStatus(rawValue:) короче, сохраняет типобезопасность и естественно обрабатывает неизвестные коды через nil.
Оптимальное решение — использовать сгенерированный failable-инициализатор, а неизвестные значения направлять в отдельную ветку обработки. Это сохраняет совместимость с будущими кодами сервера и не маскирует ошибку под существующий случай.
Вопрос: Является ли HTTPStatus(rawValue: 201) ошибкой Swift во время компиляции?
Ответ: Нет. Компилятор допускает вызов и возвращает nil во время выполнения. Компиляционная ошибка возникла бы, например, при нарушении правил самого объявления перечисления, а не при передаче неизвестного значения в failable-инициализатор.
Вопрос: Можно ли изменить rawValue у уже созданного случая перечисления?
Ответ: Нет. rawValue описывает фиксированное соответствие случая внешнему значению и доступно только для чтения. Чтобы получить другой статус, нужно создать другой случай перечисления, а не изменить свойство существующего.
Вопрос: Можно ли добавить связанные значения к перечислению, которое имеет raw-тип Int?
Ответ: Нет. Перечисление с raw-значениями представляет каждый случай одним фиксированным исходным значением, тогда как связанные значения позволяют хранить данные, зависящие от конкретного экземпляра случая. Для вариантов вроде case failure(code: Int) используют обычное перечисление без raw-типа и при необходимости реализуют собственное преобразование вручную.