Elixir - Gestion des erreurs

Elixir a trois mécanismes d'erreur: les erreurs, les lancers et les sorties. Explorons chaque mécanisme en détail.

Erreur

Des erreurs (ou exceptions) sont utilisées lorsque des choses exceptionnelles se produisent dans le code. Un exemple d'erreur peut être récupéré en essayant d'ajouter un nombre dans une chaîne -

IO.puts(1 + "Hello")

Lorsque le programme ci-dessus est exécuté, il produit l'erreur suivante -

** (ArithmeticError) bad argument in arithmetic expression
   :erlang.+(1, "Hello")

C'était un exemple d'erreur intégrée.

Relever des erreurs

nous pouvons raiseerreurs en utilisant les fonctions de montée. Prenons un exemple pour comprendre la même chose -

#Runtime Error with just a message
raise "oops"  # ** (RuntimeError) oops

D'autres erreurs peuvent être déclenchées en passant le nom de l'erreur et une liste d'arguments de mot-clé

#Other error type with a message
raise ArgumentError, message: "invalid argument foo"

Vous pouvez également définir vos propres erreurs et les relever. Prenons l'exemple suivant -

defmodule MyError do
   defexception message: "default message"
end

raise MyError  # Raises error with default message
raise MyError, message: "custom message"  # Raises error with custom message

Récupérer les erreurs

Nous ne voulons pas que nos programmes s'arrêtent brusquement, mais les erreurs doivent plutôt être traitées avec soin. Pour cela, nous utilisons la gestion des erreurs. nousrescue erreurs en utilisant le try/rescueconstruction. Prenons l'exemple suivant pour comprendre la même chose -

err = try do
   raise "oops"
rescue
   e in RuntimeError -> e
end

IO.puts(err.message)

Lorsque le programme ci-dessus est exécuté, il produit le résultat suivant -

oops

Nous avons géré les erreurs dans l'instruction de sauvetage en utilisant la correspondance de modèle. Si nous n'avons aucune utilisation de l'erreur et que nous voulons simplement l'utiliser à des fins d'identification, nous pouvons également utiliser le formulaire -

err = try do
   1 + "Hello"
rescue
   RuntimeError -> "You've got a runtime error!"
   ArithmeticError -> "You've got a Argument error!"
end

IO.puts(err)

Lors de l'exécution du programme ci-dessus, il produit le résultat suivant -

You've got a Argument error!

NOTE- La plupart des fonctions de la bibliothèque standard Elixir sont implémentées deux fois, une fois renvoyant des tuples et l'autre provoquant des erreurs. Par exemple, leFile.read et le File.read!les fonctions. Le premier retournait un tuple si le fichier était lu avec succès et si une erreur était rencontrée, ce tuple était utilisé pour donner la raison de l'erreur. Le second a soulevé une erreur si une erreur était rencontrée.

Si nous utilisons la première approche fonctionnelle, nous devons utiliser le cas pour le modèle correspondant à l'erreur et agir en conséquence. Dans le second cas, nous utilisons l'approche try rescue pour le code sujet aux erreurs et traitons les erreurs en conséquence.

Jette

Dans Elixir, une valeur peut être lancée et plus tard être capturée. Throw et Catch sont réservés aux situations où il n'est pas possible de récupérer une valeur à moins d'utiliser throw et catch.

Les instances sont assez rares en pratique sauf lors de l'interfaçage avec des bibliothèques. Par exemple, supposons maintenant que le module Enum ne fournissait aucune API pour trouver une valeur et que nous devions trouver le premier multiple de 13 dans une liste de nombres -

val = try do
   Enum.each 20..100, fn(x) ->
      if rem(x, 13) == 0, do: throw(x)
   end
   "Got nothing"
catch
   x -> "Got #{x}"
end

IO.puts(val)

Lorsque le programme ci-dessus est exécuté, il produit le résultat suivant -

Got 26

Sortie

Lorsqu'un processus meurt de «causes naturelles» (par exemple, des exceptions non gérées), il envoie un signal de sortie. Un processus peut également mourir en envoyant explicitement un signal de sortie. Prenons l'exemple suivant -

spawn_link fn -> exit(1) end

Dans l'exemple ci-dessus, le processus lié est mort en envoyant un signal de sortie avec la valeur 1. Notez que la sortie peut également être «interceptée» en utilisant try / catch. Par exemple -

val = try do
   exit "I am exiting"
catch
   :exit, _ -> "not really"
end

IO.puts(val)

Lorsque le programme ci-dessus est exécuté, il produit le résultat suivant -

not really

Après

Parfois, il est nécessaire de s'assurer qu'une ressource est nettoyée après une action qui peut potentiellement provoquer une erreur. La construction try / after vous permet de faire cela. Par exemple, nous pouvons ouvrir un fichier et utiliser une clause after pour le fermer, même si quelque chose ne va pas.

{:ok, file} = File.open "sample", [:utf8, :write]
try do
   IO.write file, "olá"
   raise "oops, something went wrong"
after
   File.close(file)
end

Lorsque nous exécutons ce programme, il nous donnera une erreur. Mais leafter garantit que le descripteur de fichier est fermé lors d'un tel événement.