1️⃣ 核心区别
| 特性 | try/catch | Result<T, Error> |
|---|---|---|
| 错误处理方式 | 抛出异常,由调用方捕获 | 错误作为值封装在枚举中,沿函数链传递 |
| 函数签名 | 通过 throws 声明 | 返回类型 Result<Success, Failure> |
| 函数式组合 | 不方便链式组合,需要嵌套 do/catch | 支持 map, flatMap, mapError 链式组合 |
| 纯函数兼容性 | 异常属于副作用,不是纯函数 | 函数返回值包含错误,保持纯函数特性 |
| 错误可控性 | 异常随时抛出,可能中断流程 | 错误显式传递,可选择继续或停止流程 |
| 并发安全 | 异常中断线程上下文,需小心 | 可安全在并发流水线中传递,无副作用 |
2️⃣ 使用对比示例
假设我们有一个字符串转 Int,并进行加法操作。
(1) try/catch 风格
enum ParseError: Error {
case invalidNumber
}
func parseIntThrow(_ str: String) throws -> Int {
guard let n = Int(str) else { throw ParseError.invalidNumber }
return n
}
do {
let value = try parseIntThrow("123")
let result = value + 10
print("Result:", result)
} catch {
print("Error:", error)
}
-
优点:语法直观
-
缺点:
- 不方便函数组合
- 链式调用时需要多层
do/catch - 异常中断流程 → 不适合函数式流水线
(2) Result<T, Error> 风格
func parseIntResult(_ str: String) -> Result<Int, ParseError> {
if let n = Int(str) {
return .success(n)
} else {
return .failure(.invalidNumber)
}
}
// 链式处理
let result = parseIntResult("123")
.map { $0 + 10 } // 成功则加 10
switch result {
case .success(let value):
print("Result:", value)
case .failure(let error):
print("Error:", error)
}
-
优点:
- 链式组合自然 (
map,flatMap) - 错误显式传递 → 函数式友好
- 核心逻辑保持纯函数 → 易测试、易复用
- 链式组合自然 (
-
缺点:
- 需要显式处理错误值
- 对简单抛异常场景稍显冗长
3️⃣ 使用场景建议
| 场景 | 建议 |
|---|---|
| 简单函数调用,错误可直接中断 | try/catch 更直观 |
| 函数式流水线 / 链式组合 | Result 更适合 |
| 并发处理 / 数据流水线 | Result 可安全传递,不中断线程 |
| 纯函数式设计 / 测试驱动 | Result 优先,保持核心逻辑纯净 |
| 多层调用,错误需要统一处理 | Result 可通过 mapError 统一映射 |
4️⃣ 核心总结
-
try/catch
- 更适合命令式编程
- 异常中断流程 → 易写、直观
- 不利于函数式组合
-
Result<T, Error>
- 错误显式传递 → 保持函数式纯净
- 支持链式组合 →
map,flatMap,mapError - 天然并发安全,可组合数据流水线
⚡ 经验法则:函数式设计中,优先使用
Result来处理可能失败的操作,核心逻辑保持纯函数;将 try/catch 仅用于外层副作用或必须中断的场景。