Après avoir cherché quelques références pour le comprendre, malheureusement, je ne trouvais pas de description utile et simple sur la compréhension des différences entre throws
et rethrows
. C'est un peu déroutant d'essayer de comprendre comment nous devrions les utiliser.
Je mentionnerais que je connais un peu le -default- throws
avec sa forme la plus simple pour propager une erreur, comme suit:
enum CustomError: Error {
case potato
case tomato
}
func throwCustomError(_ string: String) throws {
if string.lowercased().trimmingCharacters(in: .whitespaces) == "potato" {
throw CustomError.potato
}
if string.lowercased().trimmingCharacters(in: .whitespaces) == "tomato" {
throw CustomError.tomato
}
}
do {
try throwCustomError("potato")
} catch let error as CustomError {
switch error {
case .potato:
print("potatos catched") // potatos catched
case .tomato:
print("tomato catched")
}
}
Jusqu'ici tout va bien, mais le problème se pose lorsque:
func throwCustomError(function:(String) throws -> ()) throws {
try function("throws string")
}
func rethrowCustomError(function:(String) throws -> ()) rethrows {
try function("rethrows string")
}
rethrowCustomError { string in
print(string) // rethrows string
}
try throwCustomError { string in
print(string) // throws string
}
ce que je sais jusqu’à présent, c’est lorsque j’appelle une fonction que throws
elle doit être gérée par un try
, contrairement à rethrows
. Et alors?! Quelle logique faut-il suivre pour décider d'utiliser throws
ou rethrows
?
De "Déclarations" dans le Swift book:
Fonctions et méthodes de renvoi
Une fonction ou une méthode peut être déclarée avec le mot-clé
rethrows
pour indiquer qu’elle génère une erreur uniquement si l’un de ses paramètres de fonction génère une erreur. Ces fonctions et méthodes sont appelées fonctions de réadressage et méthodes de réadressage. Les fonctions et méthodes de renvoi doivent avoir au moins un paramètre de fonction de projection.
Un exemple typique est la méthode map
:
public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
Si map
est appelé avec une transformation sans projection, elle ne renvoie pas d'erreur elle-même et peut être appelée sans try
:
// Example 1:
let a = [1, 2, 3]
func f1(n: Int) -> Int {
return n * n
}
let a1 = a.map(f1)
Mais si map
est appelé avec une fermeture, alors lui-même peut lancer et doit être appelé avec try
:
// Example 2:
let a = [1, 2, 3]
enum CustomError: Error {
case illegalArgument
}
func f2(n: Int) throws -> Int {
guard n >= 0 else {
throw CustomError.illegalArgument
}
return n*n
}
do {
let a2 = try a.map(f2)
} catch {
// ...
}
map
était déclaré comme throws
au lieu de rethrows
, vous devrez alors l'appeler avec try
même dans l'exemple 1, ce qui est "gênant" et gonfle le code inutile.map
a été déclaré sans throws/rethrows
, Vous ne pouvez pas l'appeler avec une fermeture à la projection comme dans l'exemple 2.Il en va de même pour les autres méthodes de la bibliothèque standard Swift qui prennent les paramètres de la fonction: filter()
, index(where:)
, forEach()
et beaucoup beaucoup plus.
Dans ton cas,
func throwCustomError(function:(String) throws -> ()) throws
désigne une fonction qui peut renvoyer une erreur, même si elle est appelée avec un argument non-jetable, alors que
func rethrowCustomError(function:(String) throws -> ()) rethrows
dénote une fonction qui lève une erreur uniquement si elle est appelée avec un argument de projection.
Grosso modo, rethrows
est destiné aux fonctions qui ne génèrent pas d’erreurs "seules", mais uniquement des erreurs "de transfert" de leurs paramètres de fonction.
Juste pour ajouter quelque chose avec la réponse de Martin. Une fonction non lancée avec la même signature qu'une fonction lancée est considérée comme un sub-type
de la fonction de projection. C’est la raison pour laquelle les rethrows peuvent déterminer lequel il s’agit et ne requièrent que try
lorsque le paramètre func le lance également, tout en acceptant la même signature de fonction que celle qui n’exécute pas. C'est un moyen pratique de n'utiliser qu'un bloc do try lorsque le paramètre func est lancé, mais l'autre code de la fonction ne génère pas d'erreur.