Swift 中的 throws 和 rethrows 有什么区别?
What are the differences between throws and rethrows in Swift?
在搜索了一些参考资料来弄清楚之后,-不幸的是-我找不到有用且简单的描述来理解 throws
和 rethrows
之间的差异。当试图理解我们应该如何使用它们时,这有点令人困惑。
我想提一下,我有点熟悉 -default- throws
及其传播错误的最简单形式,如下所示:
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")
}
}
到目前为止一切顺利,但问题出现在:
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
}
到目前为止,我所知道的是,当调用 throws
函数时,它必须由 try
处理,这与 rethrows
不同。所以呢?!我们在决定使用throws
或rethrows
时应该遵循什么逻辑?
来自 Swift 书中的 "Declarations":
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.
一个典型的例子是map
方法:
public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
如果使用非抛出转换调用 map
,它不会抛出
一个错误本身,可以在没有 try
:
的情况下调用
// Example 1:
let a = [1, 2, 3]
func f1(n: Int) -> Int {
return n * n
}
let a1 = a.map(f1)
但是如果 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 {
// ...
}
- 如果
map
被声明为 throws
而不是 rethrows
那么你会
即使在示例 1 中也必须使用 try
来调用它,
这是 "inconvenient" 并且使代码变得不必要。
- 如果在没有
throws/rethrows
的情况下声明 map
那么你不能
像示例 2 一样使用抛出闭包调用它。
Swift标准库中的其他方法也是如此
它采用函数参数:filter()
、index(where:)
、forEach()
等等。
在你的情况下,
func throwCustomError(function:(String) throws -> ()) throws
表示一个函数可能会抛出错误,即使使用
一个非抛出参数,而
func rethrowCustomError(function:(String) throws -> ()) rethrows
表示一个函数只有在调用时才会抛出错误
抛出参数。
粗略地说,rethrows
用于不抛出异常的函数
错误 "on their own",但函数只有 "forward" 个错误
参数。
只是为了补充 Martin 的回答。与抛出函数具有相同签名的非抛出函数被认为是抛出函数的sub-type
。这就是为什么 rethrows 可以确定它是哪一个并且只需要 try
当 func 参数也抛出时,但仍然接受不抛出的相同函数签名。当 func 参数抛出时,只需要使用 do try 块是一种方便的方法,但函数中的其他代码不会抛出错误。
在搜索了一些参考资料来弄清楚之后,-不幸的是-我找不到有用且简单的描述来理解 throws
和 rethrows
之间的差异。当试图理解我们应该如何使用它们时,这有点令人困惑。
我想提一下,我有点熟悉 -default- throws
及其传播错误的最简单形式,如下所示:
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")
}
}
到目前为止一切顺利,但问题出现在:
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
}
到目前为止,我所知道的是,当调用 throws
函数时,它必须由 try
处理,这与 rethrows
不同。所以呢?!我们在决定使用throws
或rethrows
时应该遵循什么逻辑?
来自 Swift 书中的 "Declarations":
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.
一个典型的例子是map
方法:
public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
如果使用非抛出转换调用 map
,它不会抛出
一个错误本身,可以在没有 try
:
// Example 1:
let a = [1, 2, 3]
func f1(n: Int) -> Int {
return n * n
}
let a1 = a.map(f1)
但是如果 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 {
// ...
}
- 如果
map
被声明为throws
而不是rethrows
那么你会 即使在示例 1 中也必须使用try
来调用它, 这是 "inconvenient" 并且使代码变得不必要。 - 如果在没有
throws/rethrows
的情况下声明map
那么你不能 像示例 2 一样使用抛出闭包调用它。
Swift标准库中的其他方法也是如此
它采用函数参数:filter()
、index(where:)
、forEach()
等等。
在你的情况下,
func throwCustomError(function:(String) throws -> ()) throws
表示一个函数可能会抛出错误,即使使用 一个非抛出参数,而
func rethrowCustomError(function:(String) throws -> ()) rethrows
表示一个函数只有在调用时才会抛出错误 抛出参数。
粗略地说,rethrows
用于不抛出异常的函数
错误 "on their own",但函数只有 "forward" 个错误
参数。
只是为了补充 Martin 的回答。与抛出函数具有相同签名的非抛出函数被认为是抛出函数的sub-type
。这就是为什么 rethrows 可以确定它是哪一个并且只需要 try
当 func 参数也抛出时,但仍然接受不抛出的相同函数签名。当 func 参数抛出时,只需要使用 do try 块是一种方便的方法,但函数中的其他代码不会抛出错误。