46 votes

Pourquoi se "lance" pas de type coffre-fort dans Swift?

La plus grande incompréhension pour moi dans Swift est l' throws mot-clé. Envisager le morceau de code suivant:

func myUsefulFunction() throws

Nous ne pouvons pas vraiment comprendre ce genre d'erreur, il va le jeter. La seule chose que nous savons, c'est qu'il pourrait jeter une erreur. La seule façon de comprendre ce que l'erreur peut-être, c'est en regardant la documentation ou la vérification de l'erreur à l'exécution.

Mais n'est-ce pas à l'encontre de Swift de la nature? Swift a de puissants médicaments génériques et un système de type pour rendre le code expressif, pourtant, il se sent comme si throws est exactement à l'opposé parce que vous ne pouvez pas obtenir quoi que ce soit à propos de l'erreur à partir de la recherche à la signature de la fonction.

Pourquoi donc? Ou ai-je raté quelque chose d'important et il a pris le concept?

31voto

Rob Napier Points 92148

J'ai été très tôt promoteur de tapé des erreurs dans Swift. C'est la façon dont l'équipe Swift m'a convaincu que j'avais tort.

Fortement typé erreurs sont fragiles, ce qui peut conduire à une mauvaise évolution de l'API. Si l'API promet de jeter un seul de précision 3 erreurs, alors quand une quatrième condition d'erreur se produit dans une version ultérieure, j'ai le choix: je l'enterrer quelque sorte dans l'existant 3, ou je me force à tous les appelant à réécrire leur code de gestion d'erreur de traiter avec elle. Puisqu'il n'était pas dans l'original 3, il n'est probablement pas un trouble très fréquent, et cela met une forte pression sur les Api de ne pas développer leur liste d'erreurs, en particulier une fois qu'un cadre a l'utilisation étendue sur une longue période (pensez: la Fondation).

Bien sûr, avec l'ouvrir enums, nous pouvons éviter qu', mais ouvert enum atteint aucun des objectifs d'un typage fort d'erreur. C'est fondamentalement un non erreur à nouveau parce que vous avez encore besoin d'un "défaut".

Vous pouvez toujours dire "au moins, je sais d'où vient l'erreur de ouvert avec une enum," mais ce qui tend à aggraver les choses. Dire que j'ai un système d'enregistrement et qu'il tente d'écrire et obtient un IO erreur. Que devrait-il revenir? Swift n'a pas de types de données algébriques (je ne peux pas dire () -> IOError | LoggingError), alors je serais probablement avoir à envelopper IOError en LoggingError.IO(IOError) (ce qui forces chaque couche explicitement de le renvoyer à la ligne; vous ne pouvez pas avoir d' rethrows très souvent). Même si elle était ADTs, voulez-vous vraiment IOError | MemoryError | LoggingError | UnexpectedError | ...? Une fois que vous avez un peu de couches, je me retrouve avec la couche sur la couche de l'emballage de certains sous-jacent "root cause" qui doivent être douloureusement déballé à traiter.

Et comment allez-vous gérer cela? Dans l'écrasante majorité des cas, ce qui ne les blocs catch ressembler?

} catch {
    logError(error)
    return
}

Il est extrêmement rare que les programmes de Cacao (c'est à dire des applications ("apps") pour creuser profondément dans la cause exacte de l'erreur et effectuer différentes opérations en fonction de chaque cas précis. Il y a peut être un ou deux cas qui ont une récupération, et le reste sont des choses que vous ne pouviez rien faire de toute façon. (Ce qui est un problème commun en Java avec checked exception qui ne sont pas seulement Exception; ce n'est pas comme nul n'est allé dans cette voie avant. J'aime Yegor Bugayenko arguments pour les exceptions en Java , qui a essentiellement fait valoir que son préféré Java pratique exactement la solution Swift.)

Ce n'est pas à dire qu'il n'y a pas de cas où fortement typé erreurs serait extrêmement utile. Mais il y a deux réponses à cela: tout d'abord, vous êtes libre de mettre en œuvre fortement typé erreurs sur votre propre avec un enum et très bien compilateur application de la loi. Il n'est pas parfait (vous avez encore besoin d'un défaut de capture à l'extérieur de l'instruction switch, mais pas à l'intérieur), mais assez bon si vous suivez certaines conventions sur votre propre.

Deuxièmement, si ce cas d'utilisation se révèle être important (et il peut), il n'est pas difficile d'ajouter fortement typé erreurs plus tard pour les cas sans casser les cas courants qui veulent assez générique d'erreur de manipulation. Ils devraient ajouter de la syntaxe:

func something() throws MyError { }

Et les appelants auraient à traiter que comme un type fort.

Le dernier de tous, pour fortement typé erreurs pour être d'une grande utilité, la Fondation serait nécessaire de les jeter, car il est le plus grand producteur d'erreurs dans le système. (Combien de fois avez-vous vraiment créer un NSError à partir de zéro par rapport à traiter avec celui généré par la Fondation?) Que serait une refonte massive de la Fondation et de très dur de rester compatible avec du code existant et ObjC. Donc tapé les erreurs doivent être absolument fantastique à la résolution de très commun de Cacao problèmes à considérer que le comportement par défaut. Il ne pouvait pas être juste un peu plus agréable (sans parler d'avoir des problèmes décrits ci-dessus).

Donc, rien de tout cela est de dire que non typée erreurs sont 100% solution parfaite pour la gestion des erreurs dans tous les cas. Mais ces arguments m'ont convaincu que c'était la bonne façon d'aller dans Swift aujourd'hui.

23voto

JeremyP Points 46808

Le choix est délibéré décision de conception.

Ils ne veulent pas de la situation où vous n'avez pas besoin de déclarer exception jeter comme en Objective-C, C++ et C#, parce que les appelants doivent assumer toutes les fonctions de lancer des exceptions et comprennent standard pour gérer les exceptions qui pourraient ne pas se produire, ou de simplement ignorer la possibilité d'exceptions. Aucune de ces sont idéal et le deuxième fait exception inutilisable sauf pour le cas où vous voulez à la fin du programme, parce que vous ne pouvez pas garantir que chaque fonction dans la pile d'appel a correctement libéré des ressources lorsque la pile est déroulée.

L'autre extrême est l'idée que vous avez défendu et que chaque type d'exception peuvent être déclarés. Malheureusement, les gens semblent s'opposer à la suite de ce qui est que vous avez un grand nombre de blocs catch, de sorte que vous pouvez gérer chaque type d'exception. Ainsi, par exemple, en Java, ils vont jeter Exception la réduction de la situation de la même façon que nous avons de Swift ou, pire, ils l'utilisation incontrôlée des exceptions de sorte que vous pouvez ignorer complètement le problème. Le GSON bibliothèque est un exemple de cette dernière approche.

Nous avons choisi d'utiliser décoché exceptions pour indiquer un échec d'analyse. Ceci est principalement fait parce que d'habitude, le client ne pourra pas récupérer de la mauvaise entrée, et donc les forcer à attraper un checked exception bâclée code de la capture() de bloc.

https://github.com/google/gson/blob/master/GsonDesignDocument.md

C'est une monumentale de mauvaise décision. "Salut, vous ne pouvez pas leur faire confiance pour faire votre propre gestion des erreurs, de sorte que votre application devrait planter à la place".

Personnellement, je pense que Swift obtient l'équilibre sur la droite. Vous avez à gérer des erreurs, mais vous n'avez pas à écrire des rames de catch états de le faire. Si ils sont allés plus loin, les gens trouveront des moyens pour contourner le mécanisme.

La pleine justification de la décision de conception est à https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst

MODIFIER

Il semble y avoir certaines personnes à avoir des problèmes avec certaines des choses que j'ai dit. Voici donc une explication.

Il existe deux grandes catégories de raisons pour lesquelles un programme peut lever une exception.

  • inattendu conditions de l'environnement externe au programme comme un IO erreur sur un fichier ou des données incorrectes. Ce sont des erreurs que l'application peut généralement gérer, par exemple en signalant l'erreur à l'utilisateur et en leur permettant de choisir une autre voie d'action.
  • Des erreurs de programmation tels que le pointeur null ou un tableau lié erreurs. La bonne façon de résoudre ces est pour le programmeur pour faire un changement de code.

Le deuxième type d'erreur ne devrait pas, en général, être pris, parce qu'ils indiquent une fausse supposition sur l'environnement que pourrait signifier le programme de données est corrompue. Il ma pas moyen de continuer en toute sécurité, de sorte que vous avez à faire avorter.

Le premier type d'erreur peut généralement être récupérés, mais dans le but de récupérer en toute sécurité, chaque frame de pile doit être déroulé correctement, ce qui signifie que la fonction correspondant à chaque frame de pile doit être conscient que les fonctions qu'il appelle peut lever une exception et prendre des mesures pour s'assurer que tout est nettoyé régulièrement si une exception est lancée, avec, par exemple, un bloc finally ou l'équivalent. Si le compilateur ne fournissons pas de support pour raconter le programmeur ils ont oublié de plan pour les exceptions, le programmeur n'a pas toujours le plan pour les exceptions et écrire de code que les fuites de ressources ou de feuilles de données dans un état incohérent.

La raison pour laquelle le gson attitude est tellement consternant, c'est parce qu'ils sont en train de dire que vous ne pouvez pas récupérer d'une erreur d'analyse (en fait, le pire, ils vous disent que vous n'avez pas les compétences pour récupérer d'une erreur d'analyse). C'est une chose ridicule d'affirmer, les gens tentent d'analyser invalid JSON fichiers de tous les temps. Est-ce une bonne chose que mon programme se bloque si quelqu'un sélectionne un fichier XML par erreur? Aucun ne l'est pas. Il doit signaler le problème et leur demander de sélectionner un autre fichier.

Et le gson chose a été, par la manière, juste un exemple de pourquoi l'utilisation incontrôlée des exceptions pour les erreurs que vous pouvez récupérer à partir de qui est mauvais. Si je veux récupérer de quelqu'un de la sélection d'un fichier XML, j'ai besoin d'attraper d'exécution de Java exceptions, mais lesquelles? Et bien je pouvait regarder dans le Gson docs pour savoir, en supposant qu'elles sont correctes et à jour. Si ils avaient disparu avec les exceptions, l'API serait dites-moi quelles sont les exceptions à attendre, et le compilateur me dire si je n'ai pas les gérer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X