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:
我想说的是,我有点熟悉-default- throw的最简单的形式来传播错误,如下所示:
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
?
到目前为止,我所知道的是调用抛出的函数时必须通过尝试来处理,这与重新抛出不同。那又怎样? !当我们决定使用抛出或重扔时,应该遵循什么逻辑?
2 个解决方案
#1
75
From "Declarations" in the Swift book:
从Swift的“宣言”中:
Rethrowing Functions and Methods
将函数和方法
A function or method can be declared with the
rethrows
keyword to indicate that it throws an error only if one of it’s function parameters throws an error. These functions and methods are known as rethrowing functions and rethrowing methods. Rethrowing functions and methods must have at least one throwing function parameter.一个函数或方法可以用retoss关键字声明,以表明只有在函数参数之一抛出错误时才抛出错误。这些函数和方法称为重扔函数和重扔方法。重新抛出函数和方法必须具有至少一个抛出函数参数。
A typical example is the map
method:
一个典型的例子是map方法:
public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
If map
is called with a non-throwing transform, it does not throw an error itself and can be called without try
:
如果使用非抛出转换调用map,则它本身不会抛出错误,并且可以在不尝试的情况下调用:
// Example 1:
let a = [1, 2, 3]
func f1(n: Int) -> Int {
return n * n
}
let a1 = a.map(f1)
But if map
is called with a throwing closure then itself can throw and must be called with try
:
但是如果用一个抛出闭包调用map,那么它本身就可以抛出,并且必须通过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 {
// ...
}
- If
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被声明为抛出而不是重新抛出,那么即使在示例1中,您也必须使用try调用它,这是“不方便的”,并且会使代码变得不必要。
- If
map
were declared withoutthrows/rethrows
then you could not call it with a throwing closure as in example 2. - 如果map是在没有抛出/重新抛出的情况下声明的,那么您就不能像示例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.
对于使用函数参数的Swift标准库的其他方法也是如此:filter()、索引(where:)、forEach()和许多其他方法。
In your case,
在你的情况下,
func throwCustomError(function:(String) throws -> ()) throws
denotes a function which can throw an error, even if called with a non-throwing argument, whereas
表示一个函数,该函数可以抛出一个错误,即使使用非抛出参数进行调用
func rethrowCustomError(function:(String) throws -> ()) rethrows
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.
粗略地说,retoss是指那些不会“自己”抛出错误的函数,而只是从函数参数中“向前”抛出错误的函数。
#2
8
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 require try
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.
只是想和马丁的回答一起补充一些东西。具有与投掷函数相同签名的非投掷函数被认为是投掷函数的子类型。这就是为什么rethrow可以确定它是哪一个,并且只需要在func param抛出时尝试,但是仍然接受不抛出的相同的函数签名。这是一种方便的方法,当func param抛出时,只需使用一个do try块,但是函数中的其他代码不会抛出错误。
#1
75
From "Declarations" in the Swift book:
从Swift的“宣言”中:
Rethrowing Functions and Methods
将函数和方法
A function or method can be declared with the
rethrows
keyword to indicate that it throws an error only if one of it’s function parameters throws an error. These functions and methods are known as rethrowing functions and rethrowing methods. Rethrowing functions and methods must have at least one throwing function parameter.一个函数或方法可以用retoss关键字声明,以表明只有在函数参数之一抛出错误时才抛出错误。这些函数和方法称为重扔函数和重扔方法。重新抛出函数和方法必须具有至少一个抛出函数参数。
A typical example is the map
method:
一个典型的例子是map方法:
public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
If map
is called with a non-throwing transform, it does not throw an error itself and can be called without try
:
如果使用非抛出转换调用map,则它本身不会抛出错误,并且可以在不尝试的情况下调用:
// Example 1:
let a = [1, 2, 3]
func f1(n: Int) -> Int {
return n * n
}
let a1 = a.map(f1)
But if map
is called with a throwing closure then itself can throw and must be called with try
:
但是如果用一个抛出闭包调用map,那么它本身就可以抛出,并且必须通过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 {
// ...
}
- If
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被声明为抛出而不是重新抛出,那么即使在示例1中,您也必须使用try调用它,这是“不方便的”,并且会使代码变得不必要。
- If
map
were declared withoutthrows/rethrows
then you could not call it with a throwing closure as in example 2. - 如果map是在没有抛出/重新抛出的情况下声明的,那么您就不能像示例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.
对于使用函数参数的Swift标准库的其他方法也是如此:filter()、索引(where:)、forEach()和许多其他方法。
In your case,
在你的情况下,
func throwCustomError(function:(String) throws -> ()) throws
denotes a function which can throw an error, even if called with a non-throwing argument, whereas
表示一个函数,该函数可以抛出一个错误,即使使用非抛出参数进行调用
func rethrowCustomError(function:(String) throws -> ()) rethrows
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.
粗略地说,retoss是指那些不会“自己”抛出错误的函数,而只是从函数参数中“向前”抛出错误的函数。
#2
8
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 require try
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.
只是想和马丁的回答一起补充一些东西。具有与投掷函数相同签名的非投掷函数被认为是投掷函数的子类型。这就是为什么rethrow可以确定它是哪一个,并且只需要在func param抛出时尝试,但是仍然接受不抛出的相同的函数签名。这是一种方便的方法,当func param抛出时,只需使用一个do try块,但是函数中的其他代码不会抛出错误。