处理保护块中的异常



我正在寻找一种在Swift中将guard块与do-try-catch语义相结合的优雅方式,到目前为止,我还没有对自己的努力感到满意。

在后台,我从一个抛出异常的函数中获得了一些必要的数据,可以返回一个可选值。作为一个具体的例子,假设我正在从存储在文件系统上的TODO列表中查找最新的项目。这可以合法地是nil(列表中没有任何内容(,也可以引发异常(访问文件系统时出现某种I/O错误(,因此这两种情况是不同的。

不过,对于方法的其余部分,我们只关心是否有一个项目可以操作——所以这听起来像是guard:的理想情况

func myCoolMethod() {
guard let todoItem = try? fetchItemFromList() else {
// No item to act on, nothing to do
return
}
// rest of function acts on todoItem
...
}

这确实很好-直到保护块在您期望它成功的时候开始失败,并且您真的很想(如果没有其他事情的话(记录错误消息以尝试和调查。因此,需要用try替换try?以捕获错误,现在我们需要将guard封装在do块中:

func myCoolMethod() {
do {
guard let todoItem = try fetchItemFromList() else {
// No item to act on, nothing to do
return
}
// rest of function acts on todoItem
...
}
catch {
// Now we know there was an error of some sort (can specialize the
// catch block if we need to be more precise)
// log error or similar, then...
return
}
}

我看到有两个问题:

  1. do块需要封装整个方法(由于todoItem的范围(。这意味着快乐路径逻辑是缩进的(这是guard特别帮助避免的(;但另外,这意味着来自该逻辑的任何错误都将被catch块捕获,可能是意外的。我更希望do块的作用域仅围绕guard
  2. 有重复的";如果我们没有项目该怎么办;块在这个琐碎的例子中,它只是一个return,但有两个单独的块需要做大致相同的事情,这仍然让我有点痛苦(其中一个块只是在错误可用的情况下添加了一点额外的上下文(

我想尽可能接近这个:

func myCoolMethod() {
// `do guard` is made up but would enter the else block either if an exception
// is thrown, or the pattern match/conditional fails
do guard let todoItem = try fetchItemFromList() else {
if let error = error {
// Optional `error` is only bound if the guard failed due to an exception
// here we can log it etc.
}
// This part is common for both the case where an exception was thrown,
// and the case where the method successfully returned `nil`, so we can
// implement the common logic for handling "nothing to act on"
return
}
// rest of function acts on todoItem
...
}

我有什么选择?

首先,查找错误并进行处理。然后寻找存在,并处理它:

func myCoolMethod() {
// Making this `let` to be stricter. `var` would remove need for `= nil` later
let todoItem: Item?
// First check for errors
do {
todoItem = try fetchItemFromList()
} catch {
todoItem = nil  // Need to set this in either case
// Error handling
}
// And then check for values
guard let todoItem = todoItem else {
// No item to act on (missing or error), nothing to do
return
}
// rest of function acts on todoItem
print(todoItem)
}

或者将问题分解为多个函数。当事情变得复杂时,制作更专注的功能:

func myCoolMethod() {
// And then check for values
guard let todoItem = fetchItemAndHandleErrors() else {
// No item to act on (missing or error), nothing to do
return
}
// rest of function acts on todoItem
print(todoItem)
}
func fetchItemAndHandleErrors() -> Item? {
// First check for errors
do {
return try fetchItemFromList()
} catch {
// Error handling
return nil
}
}

最新更新