【问题标题】: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 时,我们应该遵循什么逻辑?

【问题讨论】:

    标签: swift error-handling try-catch throws rethrow


    【解决方案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 块,但函数中的其他代码不会抛出错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-03
      • 2018-03-21
      • 2015-02-10
      • 2014-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多