Eu tentei entender o novo problema de manipulação de erros no swift 2. Aqui está o que eu fiz: declarei primeiro um enum de erro:
enum SandwichError: ErrorType {
case NotMe
case DoItYourself
}
E então eu declarei um método que gera um erro (não pessoal de exceção. É um erro). Aqui está esse método:
func makeMeSandwich(names: [String: String]) throws -> String {
guard let sandwich = names["sandwich"] else {
throw SandwichError.NotMe
}
return sandwich
}
O problema é do lado de chamada. Aqui está o código que chama esse método:
let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
}
Após o do
compilador de linha diz Errors thrown from here are not handled because the enclosing catch is not exhaustive
. Mas, na minha opinião, é exaustivo porque há apenas dois casos em SandwichError
enum.
Para declarações regulares de troca, o swift pode entender que é exaustivo quando todos os casos são tratados.
do
blocos no nível superior que não são exaustivos - se você envolver o fazer em uma função de não arremessar, o erro será gerado.Respostas:
Existem dois pontos importantes no modelo de tratamento de erros do Swift 2: exaustividade e resiliência. Juntos, eles se resumem à sua declaração
do
/catch
, precisando detectar todos os erros possíveis, não apenas aqueles que você sabe que pode lançar.Observe que você não declara quais tipos de erros uma função pode gerar, apenas se ela gera. É um problema do tipo zero-um-infinito: como alguém que define uma função para os outros (incluindo o seu futuro eu), você não precisa fazer com que cada cliente da sua função se adapte a todas as mudanças na implementação do seu função, incluindo quais erros ela pode gerar. Você deseja que o código que chama sua função seja resistente a essa alteração.
Como sua função não pode dizer que tipo de erros ela gera (ou pode gerar no futuro), os
catch
blocos que os capturam não sabem que tipos de erros podem ser lançados. Portanto, além de lidar com os tipos de erro que você conhece, você precisa lidar com os que não conhece com umacatch
instrução universal - dessa forma, se sua função alterar o conjunto de erros que ela lança no futuro, os chamadores ainda perceberão seu problema. erros.Mas não vamos parar por aí. Pense mais nessa idéia de resiliência. Do jeito que você projetou seu sanduíche, você deve descrever os erros em todos os lugares em que os usar. Isso significa que sempre que você altera o conjunto de casos de erro, é necessário alterar todos os locais que os usam ... não é muito divertido.
A idéia por trás da definição de seus próprios tipos de erro é permitir que você centralize coisas assim. Você pode definir um
description
método para seus erros:E então seu código de tratamento de erros pode solicitar que seu tipo de erro se descreva - agora todos os lugares em que você lida com erros podem usar o mesmo código e também com possíveis casos de erros futuros.
Isso também abre caminho para os tipos de erro (ou extensões neles) oferecerem suporte a outras formas de relatar erros - por exemplo, você pode ter uma extensão no seu tipo de erro que saiba como apresentar um
UIAlertController
para relatar o erro a um usuário do iOS.fonte
error caught in main()
.- Portanto, enquanto tudo o que você disse parece sensato, não posso reproduzir esse comportamento.try
expressão no código de produção, uma vez que pode causar um erro de execução e fazer com que seu aplicativo falhartry!
. Além disso, é possível argumentar que existem casos de uso "seguros" válidos para as várias operações de "força" no Swift (desembrulhar, tentar etc.), mesmo para o código de produção - se por pré-condição ou configuração você tiver eliminado com segurança a possibilidade de falha, isso poderá ser mais razoável causar um curto-circuito à falha instantânea do que escrever um código de tratamento de erros que não pode ser testado.SandwichError
classe faz sentido. No entanto, suspeito que para a maioria dos erros, a lógica de manipulação de erros não pode ser tão encapsulada. Isso ocorre porque geralmente requer o conhecimento do contexto do chamador (se deseja recuperar, tentar novamente ou relatar uma falha a montante, etc.). Em outras palavras, eu suspeito que o padrão mais comum teria que ser o de comparar com tipos de erro específicos de qualquer maneira.Eu suspeito que isso ainda não foi implementado corretamente ainda. O Guia de Programação Swift definitivamente parece sugerir que o compilador pode inferir correspondências exaustivas 'como uma declaração de chave'. Não menciona a necessidade de um general
catch
para ser exaustivo.Você também notará que o erro está na
try
linha, não no final do bloco, ou seja, em algum momento o compilador poderá identificar qualtry
instrução no bloco não manipulou tipos de exceção.A documentação é um pouco ambígua. Examinei o vídeo 'What's new in Swift' e não consegui encontrar nenhuma pista; Vou continuar tentando.
Atualizar:
Agora estamos no Beta 3 sem nenhuma dica de inferência ErrorType. Agora acredito que, se isso já foi planejado (e ainda acho que foi em algum momento), o despacho dinâmico nas extensões de protocolo provavelmente o matou.
Atualização Beta 4:
O Xcode 7b4 adicionou suporte para comentários de documentos
Throws:
, que "devem ser usados para documentar quais erros podem ser gerados e por quê". Eu acho que isso pelo menos fornece algum mecanismo para comunicar erros aos consumidores da API. Quem precisa de um sistema de tipos quando você tem documentação!Outra atualização:
Depois de passar algum tempo esperando automática
ErrorType
inferência, e trabalhar para fora o que as limitações seria desse modelo, eu mudei de idéia - isso é o que eu espero implementos da Apple em seu lugar. Essencialmente:Mais uma atualização
A lógica de tratamento de erros da Apple já está disponível aqui . Também houve algumas discussões interessantes na lista de discussão de rápida evolução . Essencialmente, John McCall se opõe aos erros de digitação porque ele acredita que a maioria das bibliotecas acabará incluindo um caso de erro genérico de qualquer maneira, e é improvável que esses erros adicionem muito ao código além do padrão (ele usou o termo 'blefe aspiracional'). Chris Lattner disse que está aberto a erros de digitação no Swift 3, se puder trabalhar com o modelo de resiliência.
fonte
Swift teme que sua declaração de caso não cubra todos os casos. Para corrigi-lo, você precisa criar um caso padrão:
fonte
catch
declarações.func method() throws(YourErrorEnum)
, ou mesmothrows(YourEnum.Error1, .Error2, .Error3)
para que você saiba o que pode ser jogadoTambém fiquei desapontado com a falta de tipo que uma função pode lançar, mas agora a recebo graças ao @rickster e vou resumir da seguinte forma: digamos que poderíamos especificar o tipo que uma função lança, teríamos algo assim:
O problema é que, mesmo que não alteremos nada no myFunctionThatThrows, se apenas adicionarmos um caso de erro ao MyError:
estamos ferrados porque nosso do / try / catch não é mais exaustivo, assim como em qualquer outro lugar onde chamamos funções que lançam MyError
fonte
catch {}
na parte inferior de cada bloco é indiscutivelmente pior. Espero que o compilador acabe por inferir os tipos de erro automaticamente onde puder, mas não consegui confirmar.Agora valide o número:
fonte
Crie enum como este:
Crie um método como:
Agora verifique se o erro existe ou não e lide com isso:
fonte