After searching for some references to figure it out, -unfortunately- I could not find useful -and simple- description about understanding the differences between throws
and rethrows
. It is kind of confusing when try to understand how we should use them.
I would mention that I am kind of familiar with the -default- throws
with its simplest form for propagating an error, as follows:
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")
}
}
So far so good, but the problem arises when:
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
}
what I know so far is when calling a function that throws
it has to be handled by a try
, unlike the rethrows
. So what?! What is logic that we should follow when deciding to use throws
or rethrows
?
From "Declarations" in the Swift book:
A typical example is the
map
method:If
map
is called with a non-throwing transform, it does not throw an error itself and can be called withouttry
:But if
map
is called with a throwing closure then itself can throw and must be called withtry
:map
were declared asthrows
instead ofrethrows
then you would have to call it withtry
even in example 1, which is "inconvenient" and bloats the code unnecessary.map
were declared withoutthrows/rethrows
then you could not call it with a throwing closure as in example 2.The same is true for other methods from the Swift Standard Library which take function parameters:
filter()
,index(where:)
,forEach()
and many many more.In your case,
denotes a function which can throw an error, even if called with a non-throwing argument, whereas
denotes a function which throws an error only if called with a throwing argument.
Roughly speaking,
rethrows
is for functions which do not throw errors "on their own", but only "forward" errors from their function parameters.Just to add something along with Martin's answer. A non throwing function with the same signature as a throwing function is considered a
sub-type
of the throwing function. That is why rethrows can determine which one it is and only requiretry
when the func param also throws, but still accepts the same function signature that doesn't throw. It's a convenient way to only have to use a do try block when the func param throws, but the other code in the function doesn't throw an error.