Je fais un essai pour comprendre la nouvelle gestion des erreurs dans swift 2. Voici ce que j'ai fait: J'ai d'abord déclaré une énumération d'erreur:
enum SandwichError: ErrorType {
case NotMe
case DoItYourself
}
Et puis j'ai déclaré une méthode qui lève une erreur (pas une exception les gens. C'est une erreur.). Voici cette méthode:
func makeMeSandwich(names: [String: String]) throws -> String {
guard let sandwich = names["sandwich"] else {
throw SandwichError.NotMe
}
return sandwich
}
Le problème vient du côté appelant. Voici le code qui appelle cette méthode:
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")
}
Après le do
compilateur de ligne dit Errors thrown from here are not handled because the enclosing catch is not exhaustive
. Mais à mon avis, il est exhaustif car il n'y a que deux cas SandwichError
enum.
Pour les instructions de commutation régulières, Swift peut comprendre qu'il est exhaustif lorsque chaque cas est traité.
do
blocs au niveau supérieur qui ne sont pas exhaustifs - si vous enveloppez le faire dans une fonction non-lancer, cela générera l'erreur.Réponses:
Le modèle de gestion des erreurs Swift 2 présente deux points importants: l'exhaustivité et la résilience. Ensemble, ils se résument à votre déclaration
do
/catch
devant attraper toutes les erreurs possibles, pas seulement celles que vous savez que vous pouvez lancer.Notez que vous ne déclarez pas les types d'erreurs qu'une fonction peut générer, mais uniquement si elle génère du tout. C'est une sorte de problème zéro-un-infini: en tant que personne définissant une fonction pour les autres (y compris votre futur moi) à utiliser, vous ne voulez pas avoir à obliger chaque client de votre fonction à s'adapter à chaque changement dans l'implémentation de votre fonction, y compris les erreurs qu'il peut générer. Vous voulez que le code qui appelle votre fonction résiste à ce changement.
Parce que votre fonction ne peut pas dire quel type d'erreurs elle génère (ou pourrait générer à l'avenir), les
catch
blocs qui interceptent les erreurs ne savent pas quels types d'erreurs elle pourrait générer. Ainsi, en plus de gérer les types d'erreur que vous connaissez, vous devez gérer ceux que vous ne connaissez pas avec unecatch
instruction universelle - de cette façon, si votre fonction modifie l'ensemble des erreurs qu'elle génère à l'avenir, les appelants verront toujours son les erreurs.Mais ne nous arrêtons pas là. Pensez encore à cette idée de résilience. La façon dont vous avez conçu votre sandwich, vous devez décrire les erreurs à chaque endroit où vous les utilisez. Cela signifie que chaque fois que vous modifiez l'ensemble des cas d'erreur, vous devez changer chaque endroit qui les utilise ... pas très amusant.
L'idée derrière la définition de vos propres types d'erreur est de vous permettre de centraliser des choses comme ça. Vous pouvez définir une
description
méthode pour vos erreurs:Et puis votre code de gestion des erreurs peut demander à votre type d'erreur de se décrire - désormais, chaque endroit où vous gérez des erreurs peut utiliser le même code et gérer d'éventuels futurs cas d'erreur.
Cela ouvre également la voie à des types d'erreur (ou à des extensions sur eux) pour prendre en charge d'autres moyens de signaler des erreurs - par exemple, vous pourriez avoir une extension sur votre type d'erreur qui sait comment présenter une
UIAlertController
pour signaler l'erreur à un utilisateur iOS.la source
error caught in main()
. - Donc, bien que tout ce que vous avez dit semble raisonnable, je ne peux pas reproduire ce comportement.try
expression forcée dans le code de production car elle peut provoquer une erreur d'exécution et provoquer le plantage de votre applicationtry!
. En outre, il existe sans doute des cas d'utilisation valides et «sûrs» pour les diverses opérations de «force» dans Swift (déballer, essayer, etc.) même pour le code de production - si, grâce à la précondition ou à la configuration, vous avez éliminé de manière fiable la possibilité d'échec, cela pourrait être plus raisonnable de court-circuiter en échec instantané que d'écrire du code de gestion des erreurs non testable.SandwichError
classe. Cependant, je soupçonne que pour la plupart des erreurs, la logique de gestion des erreurs ne peut pas être ainsi encapsulée. En effet, cela nécessite généralement la connaissance du contexte de l'appelant (s'il faut récupérer, réessayer, ou signaler un échec en amont, etc.). En d'autres termes, je soupçonne que le modèle le plus courant devrait de toute façon correspondre à des types d'erreur spécifiques.Je soupçonne que cela n'a pas encore été mis en œuvre correctement. Le Guide de programmation Swift semble définitivement impliquer que le compilateur peut déduire des correspondances exhaustives «comme une instruction switch». Il ne fait aucune mention du besoin d'un général
catch
pour être exhaustif.Vous remarquerez également que l'erreur se trouve sur la
try
ligne, et non à la fin du bloc, c'est-à-dire qu'à un moment donné, le compilateur pourra identifier quelletry
instruction du bloc a des types d'exceptions non gérés.La documentation est cependant un peu ambiguë. J'ai parcouru la vidéo «Quoi de neuf dans Swift» et je n'ai trouvé aucun indice; Je vais continuer à essayer.
Mettre à jour:
Nous sommes maintenant à la bêta 3 sans aucun indice d'inférence ErrorType. Je crois maintenant que si cela a jamais été planifié (et je pense toujours que c'était à un moment donné), l'envoi dynamique sur les extensions de protocole l'a probablement tué.
Mise à jour bêta 4:
Xcode 7b4 a ajouté la prise en charge des commentaires doc pour
Throws:
, qui «devrait être utilisé pour documenter quelles erreurs peuvent être générées et pourquoi». Je suppose que cela fournit au moins un mécanisme pour communiquer les erreurs aux consommateurs d'API. Qui a besoin d'un système de typage quand on a de la documentation!Une autre mise à jour:
Après avoir passé un certain temps en espérant automatiquement l'
ErrorType
inférence, et de travail quelles sont les limites seraient de ce modèle, j'ai changé d' avis - c'est ce que je souhaite d' Apple met en œuvre à la place. Essentiellement:Encore une autre mise à jour
La justification de la gestion des erreurs d'Apple est maintenant disponible ici . Il y a également eu des discussions intéressantes sur la liste de diffusion Swift-evolution . Essentiellement, John McCall est opposé aux erreurs de frappe car il pense que la plupart des bibliothèques finiront par inclure un cas d'erreur générique de toute façon, et qu'il est peu probable que les erreurs de frappe ajoutent beaucoup au code en dehors du passe-partout (il a utilisé le terme `` bluff ambitieux ''). Chris Lattner a déclaré qu'il était ouvert aux erreurs de frappe dans Swift 3 s'il pouvait fonctionner avec le modèle de résilience.
la source
Swift craint que votre déclaration de cas ne couvre pas tous les cas, pour y remédier, vous devez créer un cas par défaut:
la source
catch
déclarations.func method() throws(YourErrorEnum)
, ou mêmethrows(YourEnum.Error1, .Error2, .Error3)
pour savoir ce qui peut être lancéJ'ai également été déçu par le manque de type qu'une fonction peut lancer, mais je l'obtiens maintenant grâce à @rickster et je vais le résumer comme ceci: disons que nous pourrions spécifier le type qu'une fonction lance, nous aurions quelque chose comme ceci:
Le problème est que même si nous ne changeons rien dans myFunctionThatThrows, si nous ajoutons simplement un cas d'erreur à MyError:
nous sommes foutus car notre do / try / catch n'est plus exhaustif, ainsi que tout autre endroit où nous avons appelé des fonctions qui lancent MyError
la source
catch {}
en bas de chaque bloc est sans doute pire. J'espère que le compilateur finira par déduire automatiquement les types d'erreur là où il le peut, mais je n'ai pas été en mesure de confirmer.Validez maintenant le numéro:
la source
Créez une énumération comme ceci:
Créez une méthode comme:
Maintenant, vérifiez que l'erreur est là ou non et gérez-la:
la source