swift error-handling try-catch throws rethrow

¿Cuáles son las diferencias entre lanzamientos y repeticiones en Swift?



error-handling try-catch (2)

Después de buscar algunas referencias para resolverlo, desafortunadamente, no pude encontrar una descripción útil y simple sobre la comprensión de las diferencias entre throws y rethrows . Es un poco confuso cuando tratamos de entender cómo debemos usarlos.

Mencionaría que estoy bastante familiarizado con los throws -default- con su forma más simple para propagar un error, de la siguiente manera:

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") } }

Hasta ahora todo bien, pero el problema surge cuando:

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 }

lo que sé hasta ahora es cuando invoco una función que la throws , debe ser manejada por un try , a diferencia de los rethrows . ¡¿Y qué?! ¿Cuál es la lógica que debemos seguir al decidir usar throws o rethrows ?


De "Declarations" en el libro de Swift:

Relanzar funciones y métodos

Se puede declarar una función o método con la palabra clave rethrows para indicar que arroja un error solo si uno de sus parámetros de función arroja un error. Estas funciones y métodos se conocen como funciones de relanzamiento y métodos de relanzamiento . Las funciones y métodos de relanzamiento deben tener al menos un parámetro de función de lanzamiento.

Un ejemplo típico es el método de map :

public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

Si se llama al map con una transformación que no se lanza, no arroja un error en sí mismo y se puede llamar sin try :

// Example 1: let a = [1, 2, 3] func f1(n: Int) -> Int { return n * n } let a1 = a.map(f1)

Pero si el map se llama con un cierre de lanzamiento, entonces puede lanzar y debe llamarse con 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 el map se declarara como throws lugar de rethrows entonces tendría que llamarlo con try incluso en el ejemplo 1, lo cual es "inconveniente" e hincha el código innecesario.
  • Si el map se declarara sin throws/rethrows entonces no podría llamarlo con un cierre de lanzamiento como en el ejemplo 2.

Lo mismo es cierto para otros métodos de la Biblioteca estándar de Swift que toman parámetros de función: filter() , index(where:) , forEach() y muchos más.

En tu caso,

func throwCustomError(function:(String) throws -> ()) throws

denota una función que puede arrojar un error, incluso si se llama con un argumento que no arroja, mientras que

func rethrowCustomError(function:(String) throws -> ()) rethrows

denota una función que arroja un error solo si se llama con un argumento de lanzamiento.

En términos generales, rethrows es para funciones que no arrojan errores "por sí mismos", sino solo errores "hacia adelante" de sus parámetros de función.


Solo para agregar algo junto con la respuesta de Martin. Una función de no lanzamiento con la misma firma que una función de lanzamiento se considera un sub-type de la función de lanzamiento. Es por eso que las repeticiones pueden determinar cuál es y solo requieren try cuando el func param también arroja, pero aún acepta la misma firma de función que no arroja. Es una forma conveniente de tener que usar solo un bloque do try cuando se ejecuta el parámetro func, pero el otro código en la función no arroja un error.