Возможности 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) }