Swift 中的 throws 和 rethrows 有啥区别?

Posted

技术标签:

【中文标题】Swift 中的 throws 和 rethrows 有啥区别?【英文标题】:What are the differences between throws and rethrows in Swift?Swift 中的 throws 和 rethrows 有什么区别? 【发布时间】:2017-09-04 10:16:08 【问题描述】:

在搜索了一些参考资料以弄清楚之后,-不幸的是-我找不到有用且简单的描述来了解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 时,我们应该遵循什么逻辑?

【问题讨论】:

【参考方案1】:

来自 Swift 书中的"Declarations":

重新抛出函数和方法

一个函数或方法可以用rethrows关键字声明 表示仅当它的功能之一时才会引发错误 参数抛出错误。这些功能和方法被称为 重新抛出函数重新抛出方法。重新抛出函数和 方法必须至少有一个抛出函数参数。

一个典型的例子是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 被调用时带有一个 throwing 闭包,那么它本身就可以抛出 并且必须使用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 调用它, 这是“不方便的”并且使代码变得不必要。 如果在没有throws/rethrows 的情况下声明了map,那么你不能 如示例 2 所示,使用抛出的闭包调用它。

Swift 标准库中的其他方法也是如此 采用函数参数:filter()index(where:)forEach() 等等。

在你的情况下,

func throwCustomError(function:(String) throws -> ()) throws

表示一个可以抛出错误的函数,即使调用 一个非抛出的参数,而

func rethrowCustomError(function:(String) throws -> ()) rethrows

表示仅在使用 a 调用时抛出错误的函数 抛出论据。

粗略地说,rethrows 用于不抛出异常的函数 “自行”错误,但仅从其功能“转发”错误 参数。

【讨论】:

最后一句话是金! @Honey:答案的最后一句话是我的总结。 是的,这似乎更好。说rethrows 仅与闭包一起使用是否正确,而不是不需要它们? @Honey:我不完全明白你的意思。 rethrows 仅与接受可能抛出的函数参数的函数一起使用。 如果两者都做,语法会怎样? ?? 会不会是“再扔”??【参考方案2】:

只是在马丁的回答中添加一些内容。与抛出函数具有相同签名的非抛出函数被视为抛出函数的sub-type。这就是为什么 rethrows 可以确定它是哪一个,并且仅在 func 参数也抛出时才需要 try ,但仍然接受不抛出的相同函数签名。这是一种方便的方法,只需要在 func 参数抛出时使用 do try 块,但函数中的其他代码不会抛出错误。

【讨论】:

以上是关于Swift 中的 throws 和 rethrows 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

《从零开始学Swift》学习笔记(Day3)——Swift2.0之后增加的关键字

nodejs连接mysql报错throw err; // Rethrow non-MySQL errors解决方法

nodejs连接mysql报错:throw err; // Rethrow non-MySQL errors TypeError: Cannot read property 'query&

何时在 C# 中重新引发异常?

Swift学习笔记9--错误控制

Swift 60秒45 - Running throwing functions