Tous les deux try/rescue
et try/catch
sont des techniques de gestion des erreurs dans Elixir. Selon le chapitre correspondant dans le guide d'introduction.
Les erreurs peuvent être récupérées en utilisant le
try/rescue
construire
D'autre part,
throw
etcatch
sont réservés aux situations où il n'est pas possible de récupérer une valeur sauf en utilisantthrow
etcatch
.
J'ai une brève compréhension que rescue
est pour les erreurs. Alors que catch
est pour n'importe quelle valeur.
Cependant,
throw
et catch
' ?C'est une bonne question, après un peu de recherche.
Quelles sont les différences entre eux dans les détails?
Réponse de José:
Principalement, vous devez utiliser
throw
pour le contrôle de flux et réserverraise
pour les erreurs, ce qui se produit lors d'erreurs de développement ou dans des circonstances exceptionnelles.Dans Elixir, cette distinction est plutôt théorique, mais elle est importante dans certains langages comme Ruby, où l'utilisation d'erreurs/exceptions pour le contrôle de flux est coûteuse car la création de l'objet d'exception et de la trace est coûteuse.
Veuillez vérifier cette réponse Quelles situations nécessitent une prise de jet dans Elixir
Prochainement:
raise/rescue
Considérez augmenter/sauvetage pour être explicitement sur la gestion des exceptions (certaines situations inattendues comme des erreurs de programmation, un mauvais environnement, etc.).
throw/catch
Est utile dans les endroits où vous avez prévu des échecs. Les exemples classiques sont:
Le dernier:
Supposons que vous essayez d'exécuter du code à partir d'un processus supervisé par un Supervisor
mais que le processus meurt pour une raison inattendue.
try do
IO.inspect MayRaiseGenServer.maybe_will_raise
rescue
RuntimeError -> IO.puts "there was an error"
end
MayRaiseGenServer
est supervisé par un Supervisor
et pour une raison quelconque, une erreur s'est produite:
try do
IO.inspect MayRaiseGenServer.maybe_will_raise # <- Code after this line is no longer executed
Et puis vous pouvez trouver en utilisant catch une exception ici:
try do
IO.inspect MayRaiseGenServer.maybe_will_raise
catch
:exit, _ -> IO.puts "there was an error"
end
J'espère que cela clarifiera suffisamment ce que nous recherchons.
D'autres réponses couvrent déjà bien l'utilisation de raise
contre throw
.
Je vais décrire la mécanique de la façon de gérer chaque situation exceptionnelle à l'aide d'un tableau:
creating | handling with | where y is
-----------------------------------------------------
raise x | rescue y | %RuntimeError{message: x}
error(x) | rescue y | %ErlangError{original: x}
throw x | catch y | x
exit(x) | catch :exit, y | x
où error(x)
est en fait :erlang.error(x)
.
De plus, rescue
et catch/1
(Catch avec 1 argument) ne sont qu'un sucre syntaxique. Les 4 cas ci-dessus peuvent être traités avec catch/2
:
creating | handling with | where y is | and z is
-----------------------------------------------------------------
raise x | catch y, z | :error | %RuntimeError{message: x}
error(x) | catch y, z | :error | x
throw x | catch y, z | :throw | x
exit(x) | catch y, z | :exit | x
Notez l'asymétrie de gestion raise
et error
avec rescue
vs catch/2
: x
est encapsulé dans %ErlangError
Lorsque rescue
est utilisé, mais pas avec catch/2
.
En lisant la réponse de Dimagog, et l'article trouvé sur https://inquisitivedeveloper.com/lwm-elixir-48/ , j'ai vraiment acquis beaucoup de perspicacité sur la question. Je partage juste un exemple pratique personnel,
chset =
%SomeModel{}
|> SomeModel.changeset(attrs)
try do
chset
|> Repo.insert()
catch :error, %Postgrex.Error{postgres: %{code: :invalid_password}} ->
{ :error ,
chset
|> Changeset.add_error(:username, "may be invalid")
|> Changeset.add_error(:password, "may be invalid")
}
else
{:ok, lr} -> {:ok, Map.put(lr, :password, nil)}
error -> error
end
Le code d'erreur postgresql provient d'une fonction plpgsql
dans laquelle je déclenche une erreur, comme suit,
raise invalid_password using
message = 'Invalid username or password' ,
detail = 'A user could not be found that matched the supplied username and password';