Возможности Swift.Result выходят за рамки простого получения значения / ошибки с помощью предложения switch. Давайте посмотрим, что вы можете сделать
Получите значение успеха в качестве необязательного
В некоторых случаях вам просто нужно значение успеха Result. С func get() throws -> Success вам не нужно использовать условие переключения.
let resultSuccess = Result<Int, Error>.success(1) let value = try? resultSuccess.get() // value is Optional(1) let resultFailure = Result<Int, Error>.failure(someError) let value2 = try? resultFailure.get() // value is nil
Преобразование метательного выражения в результат
Бывают и другие случаи, когда у вас есть бросающее выражение (например, функция, закрытие…), но вместо этого вам нужен Result. Это ваш удачный день, потому что Result имеет init, который выявляет возникшие ошибки.
init(catching body: () throws -> Success)
enum RandomError: Error {
case noNumber
}
func random() throws -> UInt {
if Bool.random() {
return UInt.random(in: 1...20)
} else {
throw RandomError.noNumber
}
}
let randomResult: Result<UInt, Error> = Result { try random() }
Тип успеха карты
Иногда вы получаете Result<A, E> от функции, но вам нужен Result<B, E>. Вы можете сделать это с помощью переключателя, но это много кода по сравнению с тем, что вы можете сделать с помощью операции Result map:
func map<NewSuccess>(_ transform: (Success) -> NewSuccess) -> Result<NewSuccess, Failure>
let resultSuccess = Result<Int, Error>.success(1)
let stringResult: Result<String, Error> = resultSuccess.map { String($0) }
Тип ошибки карты
Точно так же иногда вы получаете Result<A, E1>, но вам нужен Result<A, E2>. Итак, угадайте, для этого есть еще одна операция карты:
func mapError<NewFailure>(_ transform: (Failure) -> NewFailure) -> Result<Success, NewFailure> where NewFailure : Error
enum CustomError: Error { case customErrorA }
let customErrorResult = Result<Int, CustomError>.failure(.customErrorA)
let errorResult: Result<Int, Error> = customErrorResult.mapError { $0 }
Тип успеха FlatMap
Допустим, вы используете функцию, возвращающую Result в Result.map. Получится красивый Result<Result<Success, Failure>, Failure>. Однако, каким бы красивым оно ни было, вы можете вместо этого захотеть простой Result<Success, Failure>. Это когда вы можете использовать flatMap:
func flatMap<NewSuccess>(_ transform: (Success) -> Result<NewSuccess, Failure>) -> Result<NewSuccess, Failure>
enum FooError: Error {
case noNumber, outOfRange
}
func random() -> Result<UInt, FooError> {
if Bool.random() {
return .success(UInt.random(in: 1...25))
} else {
return .failure(FooError.noNumber)
}
}
func factorial(input: UInt) -> Result<UInt, FooError> {
if input == 0 || input > 20 {
return .failure(.outOfRange)
} else {
let f = (2…input).reduce(1) { $0 * $1 }
return .success(f)
}
}
// Result<Result<UInt, FooError>, FooError> 👎
let mappedResult: Result<Result<UInt, FooError>, FooError> = random().map { factorial(input: $0) }
// Result<UInt, FooError> 👍
let flattenResult: Result<UInt, FooError> = random().flatMap { factorial(input: $0) }
Тип сбоя FlatMap
Result предлагает другую операцию с более сложным приложением. Допустим, вы хотите восстановиться после Result.failure, сохранив тип «Успех», но изменив тип сбоя. Тогда вы можете найти Result.flatMap полезным.
func flatMapError<NewFailure>(_ transform: (Failure) -> Result<Success, NewFailure>) -> Result<Success, NewFailure> where NewFailure : Error
enum RecoverableError: Error {
case fooError
}
func recoverableOperation() -> Result<Int, RecoverableError> {
return .failure(.fooError)
}
enum RecoveryError: Error {
case notRecovered
}
func recoverFromRecoverableError(_ recoverableError: RecoverableError) -> Result<Int, RecoveryError> {
return .failure(.notRecovered)
}
// Result<Int, RecoveryError>
let flattenErrorResult = recoverableOperation().flatMapError { recoverFromRecoverableError($0) }