Swift Result 类型怎么用?和 throws 有什么区别?
Result<Success, Failure> 是 Swift 5 引入的枚举,用两个 case 表示操作结果:.success 携带成功值,.failure 携带错误。它把错误从控制流(do-catch)变成了值——你可以像处理普通枚举一样 switch 它、map 它、存起来以后再处理。
Result 最大的价值在异步回调。completion handler 里没法用 throws 传播错误(因为回调函数本身不是 throws 的),而 Result 作为返回值天然适合这种场景:func fetch(id: String, completion: @escaping (Result<User, Error>) -> Void)。async/await 出现后,新代码直接 async throws 更清晰,Result 主要用于兼容旧接口。
Result 的常用方法:map 转换成功值(失败原样传递),flatMap 链式转换(返回新的 Result),get 把 Result 变回 throws(try result.get() 可以在 do-catch 中使用)。Result { try someThrowingFunc() } 初始化器可以方便地把 throws 函数包装成 Result。
追问
Result 和 throws 怎么选?
同步代码用 throws + do-catch 更直观,也是 Swift 惯用风格。异步回调用 Result,因为 completion handler 里的 throws 外层 catch 不到。async/await 之后,新代码统一用 async throws,Result 退居兼容层。简单说:能 throws 就 throws,必须回调就用 Result。
Result 的 map 和 flatMap 有什么区别?
map 接收 (Success) -> NewValue 闭包,只转换成功值,失败原样传递,返回 Result<NewValue, Failure>。flatMap 接收 (Success) -> Result<NewValue, NewFailure>,整个 Result 替换,适合链式调用另一个可能失败的操作。类比:map 是"成功的话转换一下",flatMap 是"成功的话再做一次可能失败的操作"。
Result 的 Failure 类型有什么坑?
泛型约束 Failure: Error,但不同 Result 的 Failure 类型不同时不能直接组合。比如 Result<Data, NetworkError> 和 Result<User, DecodeError> 做 flatMap 链会报类型不匹配。解法一:统一用 Error 作为 Failure 类型(牺牲类型精确性);解法二:用 mapError 统一错误类型;解法三:自定义 AppError 枚举把所有错误 case 收拢。
Result 怎么和 async/await 配合?
用 try await someAsyncFunc() 直接替代 Result 回调。如果必须兼容旧的 Result 接口,可以用 let result = await Result { try await someFunc() } 包装。反过来,把 Result 回调转 async/await 可以用 withCheckedContinuation 桥接。
Result 和 Optional 有什么区别?什么时候用哪个?
Optional 表示"有值或没有",没有失败原因;Result 表示"成功或失败",失败时携带错误信息。读配置文件、查缓存——只关心有没有值,用 Optional;网络请求、文件读写——需要知道失败原因,用 Result。try? 可以把 throws 函数降级为 Optional,但丢失了错误详情。
写段代码
swiftenum APIError: Error { case invalidURL case badStatus(Int) case decodingFailed } func fetchUser(id: String) -> Result<User, APIError> { guard let url = URL(string: "https://api.example.com/users/\(id)") else { return .failure(.invalidURL) } return .success(User(name: "Test")) } // 链式调用 let result = fetchUser(id: "42") .map { $0.name } .flatMap { name in fetchUser(id: name) } // Result 转 throws do { let user = try fetchUser(id: "42").get() } catch { print(error) } // 包装 throws 为 Result let result2 = Result { try JSONDecoder().decode(User.self, from: data) }