web-dev-qa-db-fra.com

Quelles sont les différences entre les lancers et les relances dans Swift?

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?

61
Ahmad F

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 {
    // ...
}
  • Si 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.
  • Si 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.

140
Martin R

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.

13
JustinM