Swift 中 `throws` 和 `rethrows` 区别

401 阅读1分钟

1. throws 关键字

  • 作用:标记函数/方法可能抛出错误,强制调用者处理错误(使用 trytry?try!)。
  • 适用场景:函数内部存在可能抛出错误的操作。
  • 代码示例
// 定义错误类型
enum NetworkError: Error {
    case invalidURL
    case requestFailed
}

// 使用 throws 的函数
func fetchData(from urlString: String) throws -> Data {
    guard let url = URL(string: urlString) else {
        throw NetworkError.invalidURL // 主动抛出错误
    }
    
    // 模拟网络请求(实际可能用 URLSession)
    if urlString.isEmpty {
        throw NetworkError.requestFailed
    }
    
    return Data() // 成功返回数据
}

// 调用 throws 函数必须处理错误
do {
    let data = try fetchData(from: "https://example.com")
    print("数据获取成功: \(data)")
} catch {
    print("错误: \(error)") // 捕获并处理错误
}

2. rethrows 关键字

  • 作用:标记函数本身不主动抛出错误,但会将传入的闭包参数的错误向上传递。
  • 适用场景:高阶函数(接受闭包作为参数),且错误来源仅限于闭包。
  • 代码示例
// 定义一个 rethrows 的高阶函数
func processNumbers(_ numbers: [Int], using closure: (Int) throws -> Int) rethrows -> [Int] {
    var results = [Int]()
    for number in numbers {
        // 调用可能抛出错误的闭包
        let processed = try closure(number)
        results.append(processed)
    }
    return results
}

// 闭包不抛出错误时,调用不需要 try
let safeNumbers = [1, 2, 3]
let doubled = processNumbers(safeNumbers) { $0 * 2 } // 直接调用,无需 try
print(doubled) // 输出 [2, 4, 6]

// 闭包抛出错误时,调用需要 try
do {
    let result = try processNumbers([1, -2, 3]) { num in
        guard num >= 0 else {
            throw NetworkError.requestFailed // 闭包抛出错误
        }
        return num * 2
    }
} catch {
    print("捕获错误: \(error)") // 输出错误
}

3. throws vs rethrows 的区别

特性throwsrethrows
错误来源函数自身或内部闭包仅来自传入的闭包参数
是否必须用 try总是需要处理错误仅在闭包抛出错误时需要 try
典型应用场景任意可能抛出错误的函数高阶函数(如 mapfilter
函数自身能否抛错可以不能,只能传递闭包的错误

4. 深入理解 rethrows

规则

  • rethrows 函数必须接受至少一个 throws 闭包作为参数。
  • 函数内部不能直接抛出自己的错误,只能传递闭包的错误。

Swift 标准库中的 rethrows

// Swift 标准库的 map 函数声明
func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
  • transform 闭包无错误时,map 可直接调用:
    [1, 2, 3].map { $0 * 2 } // 无需 try
    
  • transform 闭包有错误时,必须处理:
    try [1, 2, 3].map { num in
        guard num > 0 else { throw NetworkError.requestFailed }
        return num * 2
    }
    

5. 总结与最佳实践

  • 使用 throws:当函数自身逻辑可能抛出错误时。
  • 使用 rethrows:当函数是接受闭包的高阶函数,且错误仅来自闭包时。
  • 灵活组合:结合 trytry?try!do-catch 实现精细的错误处理。

通过合理使用 throwsrethrows,可以编写更安全、灵活的 Swift 代码!