MiddleКодЧастоЕщё не отвечали
Напишите failable init? и throwing init для одного парсящего типа — когда какой уместен?
Дайте типу RGBColor, разбирающему hex-строку "#RRGGBB", два инициализатора на одном типе.
Требования:
init?(hex:)возвращаетnilпри любом некорректном вводе, без сообщения причины.init(validating:)бросаетParseError, называя, что не так (плохая длина, не-hex символ).- Оба разбирают один формат
"#RRGGBB".
struct RGBColor {
let r, g, b: UInt8
init?(hex: String) { /* ваш код здесь */ }
init(validating hex: String) throws { /* ваш код здесь */ }
}
Напишите оба инициализатора.
Используйте init?, когда у сбоя одна неинтересная причина и nil говорит достаточно; вызывающий просто проверяет nil. Throwing init нужен, когда важна причина невалидности. init? отбрасывает причину, а throws несёт богатую пробрасываемую ошибку.
- ✗Думают, что
init?может вернуть причину сбоя, как это делаетthrows - ✗Считают, что throwing
initвозвращаетnil, а не пробрасывает ошибку - ✗Полагают, что failable-инициализаторы бывают только у классов, а не у значимых типов
- →Какой из двух лучше сочетается с
try?на месте вызова? - →Когда возврат
nilтеряет информацию, реально нужную вызывающему?
Один тип, два контракта сбоя — молчаливый и говорящий:
struct RGBColor {
let r, g, b: UInt8
init?(hex: String) {
guard let rgb = RGBColor.parse(hex) else { return nil }
(r, g, b) = rgb
}
init(validating hex: String) throws {
guard hex.hasPrefix("#"), hex.count == 7 else {
throw ParseError.badLength(hex.count)
}
guard let rgb = RGBColor.parse(hex) else {
throw ParseError.nonHexDigit
}
(r, g, b) = rgb
}
}
enum ParseError: Error { case badLength(Int), nonHexDigit }
init? вызывающий использует как RGBColor(hex: s) ?? .black — причина не нужна. init(validating:) даёт место catch, где можно показать, какое поле сломано, и локализовать сообщение. Failable-инициализатор законен и у struct, и у enum. Выбор — нужна ли вызывающему причина, а не какая форма «современнее».