在斯威夫特中,抛球和回球有什么区别?

在搜索了一些参考文献以找出答案之后,-不幸的是-我找不到关于理解 throwsrethrows之间的差异的有用且简单的描述。当我们试图理解我们应该如何使用它们时,这有点令人困惑。

我要提到的是,我有点熟悉-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不同。那又怎样!在决定使用 throwsrethrows时,我们应该遵循什么逻辑?

22168 次浏览

From "Declarations" in the Swift book:

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 its 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.

A typical example is the map method:

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:

// 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:

// 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 as throws instead of rethrows then you would have to call it with try even in example 1, which is "inconvenient" and bloats the code unnecessary.
  • If map were declared without throws/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,

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.

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.