在 Swift 中,throws
、rethrows
和 never
(Never
类型)是与错误处理相关的关键概念,它们分别用于不同场景下的错误传播和控制流。以下是它们的详细对比和用法解析:
1. throws
:声明可能抛出错误的函数
作用
- 标记一个函数、方法或闭包可能抛出错误(
Error
协议类型)。 - 调用方必须使用
try
、do-catch
或try?
/try!
处理错误。
语法
swift
func mightFail() throws -> Int {
// 可能抛出错误的代码
throw SomeError.example
return 42
}
调用方式
swift
// 方式 1: do-catch
do {
let result = try mightFail()
print(result)
} catch {
print("Error: (error)")
}
// 方式 2: try? (返回 Optional)
if let result = try? mightFail() {
print(result)
}
// 方式 3: try! (强制解包,崩溃风险)
let result = try! mightFail() // 如果抛出错误,程序崩溃
适用场景
- 任何可能因外部条件(如网络请求、文件读写)失败的函数。
- 需要明确告知调用方可能出错的接口。
2. rethrows
:重新抛出错误的函数
作用
- 标记一个高阶函数 (接受闭包作为参数的函数)可能抛出错误,但错误实际来自闭包本身。
- 函数本身不直接抛出错误,只是将闭包中的错误向上传递。
- 编译器会检查闭包是否可能抛出错误,如果闭包不抛出错误,则调用方也无需处理错误。
语法
swift
func process(_ action: () throws -> Void) rethrows {
try action() // 仅重新抛出闭包中的错误
}
调用方式
swift
// 闭包抛出错误时,需用 try 调用 process
do {
try process {
throw SomeError.example
}
} catch {
print("Error: (error)")
}
// 闭包不抛出错误时,无需 try
process {
print("No error here")
}
与 throws
的区别
特性 | throws |
rethrows |
---|---|---|
错误来源 | 函数内部可能抛出错误 | 错误仅来自闭包参数 |
调用要求 | 调用方必须处理错误(无论闭包是否抛出) | 仅当闭包抛出错误时,调用方需处理错误 |
典型场景 | 直接操作可能失败的资源(如文件读写) | 高阶函数(如 map 、forEach 的变体) |
示例:rethrows
的实际应用
swift
// 高阶函数:仅当闭包抛出错误时,才需要 try
func retry<T>(times: Int, _ action: () throws -> T) rethrows -> T {
for _ in 0..<times {
do {
return try action()
} catch {
continue
}
}
throw SomeError.maxRetriesExceeded
}
// 调用方式 1:闭包抛出错误
do {
let result = try retry(times: 3) {
throw SomeError.example
}
} catch {
print("Failed after retries: (error)")
}
// 调用方式 2:闭包不抛出错误
let success = try retry(times: 3) {
return "Success" // 无需 catch
}
3. Never
:表示永不返回的错误类型
作用
Never
是 Swift 的"底部类型"(Bottom Type),表示函数永远不会正常返回(要么无限执行,要么抛出错误/终止程序)。- 常用于标记致命错误 或无限循环的场景。
语法
swift
func crashAndBurn() -> Never {
fatalError("Something went wrong!") // 终止程序
}
与 throws
的关系
- 如果函数返回
Never
,则它必须抛出错误或终止程序(不能正常返回)。 - 编译器会强制检查所有代码路径是否满足
Never
的语义。
示例
swift
// 1. 抛出错误(满足 Never 的语义)
func impossible() -> Never {
throw SomeError.criticalFailure
}
// 2. 无限循环(也满足 Never 的语义)
func infiniteLoop() -> Never {
while true {}
}
// 3. 调用 Never 函数时,编译器知道后续代码不可达
func test() {
impossible() // 调用后,编译器认为后续代码不会执行
print("This line will never run") // ❌ 编译警告:Unreachable code
}
适用场景
- 标记不可恢复的错误(如
fatalError
、preconditionFailure
)。 - 实现无限循环或事件循环(如游戏主循环)。
4. 总结对比
关键字 | 作用 | 典型场景 |
---|---|---|
throws |
声明函数可能抛出错误,调用方必须处理 | 文件读写、网络请求等可能失败的操作 |
rethrows |
声明高阶函数可能重新抛出闭包中的错误,调用方仅在闭包抛出错误时需处理 | map 、forEach 的变体,如 retry |
Never |
表示函数永不返回(抛出错误或终止程序),编译器强制检查代码路径 | fatalError 、无限循环、不可恢复错误 |
5. 最佳实践
-
优先使用
throws
:-
对于可能因外部条件失败的函数,明确标记
throws
以提高代码安全性。 -
示例:
swiftfunc readFile(_ path: String) throws -> String { guard let content = try? String(contentsOfFile: path) else { throw FileError.notFound } return content }
-
-
谨慎使用
rethrows
:-
仅在高阶函数中需要重新抛出闭包错误时使用,避免滥用。
-
示例:
swiftfunc safeExecute(_ action: () throws -> Void) rethrows { print("Starting action...") try action() print("Action completed.") }
-
-
避免滥用
Never
:-
仅在确实需要终止程序或实现无限循环时使用。
-
示例:
swiftfunc validateInput(_ input: String) -> Never { precondition(input.count > 0, "Input cannot be empty!") }
-
通过合理使用这些关键字,可以写出更安全、更清晰的 Swift 错误处理代码。