¿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 comothrows
lugar derethrows
entonces tendría que llamarlo contry
incluso en el ejemplo 1, lo cual es "inconveniente" e hincha el código innecesario. -
Si el
map
se declarara sinthrows/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.