作为 Swift 5 最值得期待的新功能之一,Result 类型该如何使用?

阅读数:6860 2019 年 1 月 15 日 12:00

作为Swift 5最值得期待的新功能之一,Result类型该如何使用?

Swift 5最值得期待的新功能之一 Result 终于进入到了该语言中来了。Result 类型强制程序员在能够访问到实际值之前显式地处理失败和成功的情况。让我们来看看它是怎么实现的,你可以怎么使用它,为什么我们需要它。

Result 类型强制用一种安全的方法来处理函数调用返回的错误,而不需要抛出异常。尽管它通过异常,提供了一种自动化的机制来进行错误的传递和处理,但是 Result 类型提供了一种手动机制,它有更强大的类型保障,并更适合于异步错误处理。与 Option类型相似,Result 类型是一个 monad

Swift 5 中的 Result 类型作为枚举实现有两种情况:.success 和.failure。

复制代码
public enum Result<Success, Failure: Error> {
/// 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() -> Result<Int, AnErrorType> {
...
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()) 会返回一个 <Result<Result<…>>。但有了 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 稳定性

查看英文原文 Previewing Swift 5 Result Type

收藏

评论

微博

用户头像
发表评论

注册/登录 InfoQ 发表评论