BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Notícias Preview do Result Type do Swift 5

Preview do Result Type do Swift 5

Favoritos

O Result, uma das propostas mais esperadas para o Swift 5, chegou à linguagem. O Result type força o programador a tratar explicitamente os casos de falha e sucesso antes que eles possam obter acesso ao valor real. Vamos ver como é implementado-ló, usá-lo e por que foi necessário.

O Result type destina-se a impor uma maneira segura de manipular erros retornados por uma chamada de função sem recorrer a exceções. Enquanto as exceções fornecem um mecanismo automático para propagação e manipulação de erros, o Result fornece um mecanismo manual com garantias de tipos mais fortes e mais adequado para o tratamento de erros assíncronos. Similarmente ao tipo Option , o Result type é uma monad.

O Result type no Swift 5 é implementado como um enum com dois casos: .success e .failure .

public enum Result<Success, Failure: Error> {
  /// A success, storing a `Success` value.
  case success(Success)

   /// A failure, storing a `Failure` value.
  case failure(Failure)
  ...
}

É possível definir uma função simples retornando um Result e manipular seu resultado da seguinte forma:


enum AnErrorType: Error {
    case failureReason1
    case failureReason2
}

func failableFunction() -> Result<Int, AnErrorType> {

    ...
    if (errorCondition1) {
        return .failure(.failureReason1)
    }
    ...
    return .success(1)
}

func callFailableFunction() {

    ...
    let result = failableFunction()
    switch result {
    case .success(let integerResult):
        ...
    case .failure(let error):
        switch error {
        case .failureReason1:
            ...
        case .failureReason2:
            ...
        }
    }
    ...
}

Além disso, para facilitar a integração com funções existentes, o tipo Result suporta um inicializador específico que aceita uma closure que pode lançar:

let result = Result { try String(contentsOfFile: configuration) }
  • map () ( mapError () ) permite a transformação automática de um valor (erro) através de uma closure, mas apenas em caso de sucesso (falha), caso contrário o <pre > Result é deixado sem modificações.
  • flatMap () ( flatMapError () ) é útil quando você quer transformar seu valor (erro) usando uma closure que retorna ele mesmo um Result para manipular caso quando a transformação falhe. Em tais casos, map () ( mapError () ) retornaria um <Result <Result <... >> . Com o flatMap () ( flatMapError () ), se obtém um Result simples e reduzido.

Os tipos Result representam uma melhoria em relação à sintaxe do try catch throw , que é o mecanismo básico para o tratamento de erros no Swift desde o Swift 2, por diversas razões.

Em primeiro lugar, usando os tipos Result, torna-se muito mais natural lidar com falhas assíncronas. Um padrão típico para manipular chamadas de função assíncronas no Swift faz uso de retornos de chamada, como no exemplo a seguir:

asyncOperationCall() { (value, error) in

    guard error != nil else { self.handleError(error!) }
    self.handleValue(value)
}

Como é possível reconhecer facilmente, o uso de retornos de chamada acaba com a finalidade das exceções no Swift, que é a propagação automática e o tratamento de erros. Em vez disso, com retornos de chamada assíncronos, o erro deve ser tratado no lugar, porque quando uma exceção pode ser lançada de um retorno de chamada já é tarde demais para o erro ser propagado automaticamente até a pilha de chamadas. Pelo contrário, um Result pode ser armazenado e processado em um momento posterior quando alguém tenta usar o valor retornado. Essa propriedade, chamada de tratamento de erro atrasado, não é específica do código assíncrono e pode simplificar muito a sintaxe do Swift, conforme mostrado no exemplo a seguir:

do {
    handleOne(try String(contentsOfFile: oneFile))
} catch {
    handleOneError(error)
}

do {
    handleTwo(try String(contentsOfFile: twoFile))
} catch {
    handleTwoError(error)
}

do {
    handleThree(try String(contentsOfFile: threeFile))
} catch {
    handleThreeError(error)
}

O código acima se traduz no seguinte trecho muito mais legível no Swift 5:

let one = Result { try String(contentsOfFile: oneFile) }
let two = Result { try String(contentsOfFile: twoFile) }
let three = Result { try String(contentsOfFile: threeFile) }

handleOne(one)
handleTwo(two)
handleThree(three)

Outra vantagem do Result é que sabemos com certeza que recebemos um erro ou um valor. Ao usar um retorno de chamada, temos quatro combinações diferentes nas quais devemos pensar: um valor sem erro; nenhum valor com um erro; um valor com um erro; sem valor e sem erro.

Finalmente, usar um tipo Result permite restringir o tipo de erro exato que pode ser retornado. No primeiro exemplo que fornecemos acima, essa propriedade nos permitiu enumerar completamente as possíveis causas de erro, ou seja, .failureReason1 e .failureReason2 , ao longo do caminho de tratamento de erros. Pelo contrário, uma função Swift que pode lançar não especifica o tipo que pode ser lançado, então ao manipular a condição de erro em um bloco catch , você só sabe que seu erro está de acordo com o Error protocolo.

O Swift 5 está previsto para ser lançado no início de 2019 e se concentra em trazer a estabilidade do ABI para a linguagem.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT