Swift 5最值得期待的新功能之一Result终于进入到了该语言中来了。Result类型强制程序员在能够访问到实际值之前显式地处理失败和成功的情况。让我们来看看它是怎么实现的,你可以怎么使用它,为什么我们需要它。
Result类型强制用一种安全的方法来处理函数调用返回的错误,而不需要抛出异常。尽管它通过异常,提供了一种自动化的机制来进行错误的传递和处理,但是Result类型提供了一种手动机制,它有更强大的类型保障,并更适合于异步错误处理。与Option类型相似,Result类型是一个monad。
Swift 5中的Result类型作为枚举实现有两种情况:.success和.failure。
public enum Result\u0026lt;Success, Failure: Error\u0026gt; { /// A success, storing a `Success` value. case success(Success) /// A failure, storing a `Failure` value. case failure(Failure) ...}
你可以定义一个简单的函数,返回Result并按如下方式处理它的结果:
enum AnErrorType: Error { case failureReason1 case failureReason2}func failableFunction() -\u0026gt; Result\u0026lt;Int, AnErrorType\u0026gt; { ... if (errorCondition1) { return .failure(.failureReason1) } ... return .success(1)}func callFailableFunction() { ... let result = failableFunction() switch result { case .success(let integerResult): ... case .failure(let error): switch error { case .failureReason1: ... case .failureReason2: ... } } ...}
此外,为了更便于和现有的函数集成,Result类型支持特定的初始化程序,它可以接受可能扔出的闭包:
let result = Result { try String(contentsOfFile: configuration) }
作为monad,Result实现已知的map()和flatMap()方法(以及理论上相同的mapError()和flatMapError()):
- map() (mapError())通过闭包实现自动值转换,但这仅仅在成功(或失败)的情况下发生,否则Result就保持不变。
- 当你想要使用闭包来转换你的值(错误)的时候,flatMap() (flatMapError())很有用,因为闭包会返回一个Result来处理转换失败的情况。在这种情况下,map() (mapError())会返回一个\u0026lt;Result\u0026lt;Result\u0026lt;…\u0026gt;\u0026gt;。但有了flatMap() (flatMapError()),你就能得到简单的Result。
Result类型代表了对自Swift 2开始Swift中简单的错误处理机制的do、try、catch、throw语法的改进,有多个原因。
首先,使用Result类型处理异步失败变得更加自然。在Swift中典型的处理异步函数调用的模式是使用回调,如下例所示:
asyncOperationCall() { (value, error) in guard error != nil else { self.handleError(error!) } self.handleValue(value)}
你可能很简单就能发现,使用回调会破坏Swift的异常目的,即自动传递和处理错误。相反,有了异步回调,错误必须就地处理,因为当回调抛出异常的时候,错误再自动传递到调用堆栈就已经太迟了。相反,当其他人尝试使用返回值的时候,Result可能会存储,并在稍后再进行处理。这种属性称为有延迟错误处理,不专属于异步代码,会大大简化Swift的语法,如下面的例子所示:
do { handleOne(try String(contentsOfFile: oneFile))} catch { handleOneError(error)}do { handleTwo(try String(contentsOfFile: twoFile))} catch { handleTwoError(error)}do { handleThree(try String(contentsOfFile: threeFile))} catch { handleThreeError(error)}
上面的代码在Swift 5中转换为更容易阅读的代码片段,如下所示:
let one = Result { try String(contentsOfFile: oneFile) }let two = Result { try String(contentsOfFile: twoFile) }let three = Result { try String(contentsOfFile: threeFile) }handleOne(one)handleTwo(two)handleThree(three)
Result的另外一个优势是我们知道我们要么得到错误,要么得到值。当使用回调的时候,我们可能得到四种类型的组合:没有错误的好的值、有错误且没有值、有值但有错误、没有值且没有错误。
最后,使用Result类型可以限制可能返回的错误类型。在上面提供的第一个例子里,这个属性帮助我们能够根据错误处理路径完全枚举可能的错误原因,比如说.failureReason1和.failureReason2。相反,Swift的函数抛出的错误并没有指定类型,因此当处理catch块中的错误条件时,你只能知道错误符合Error协议。
Swift 5计划在2019年初发布,目标是给语言带来ABI稳定性。