Swift 5.3 新特性精讲(2):多模式catch子句,再也不麻烦switch

在介绍多模式catch子句以前,咱们先来复习一下平时是怎么catch的。面试

单模式 catch 子句

有如下函数:swift

enum IOError: Error {
  case diskError(code:Int)
  case networkError(code:Int)
}

func foo(a: Int) throws -> Int {
  if a == 0 {
    return 0
  }
  throw IOError.networkError(code: 3)
}
复制代码

在使用带有 throws 的函数的时候咱们必须 try:除了 try?用于不成功便成空,try!用于不成功便崩溃(不多用)以外,最经常使用的就是try的语句了。函数

一把梭catch:ui

do {
  try print(foo(a: 1))
} catch {
  print("error occurred")
}
复制代码

一把梭catch,并进行变量绑定:spa

do {
  try print(foo(a: 1))
} catch let err {
  print("error occurred \(err)")
}
复制代码

值得注意的是,若是当前的函数签名不带 throws, 则 catch 必须 exhaustive 穷举。可是你可能会问下面这个一把梭 catch 真的须要吗?函数foo不是只会抛出一种错误IOError吗?.net

func myFunc() {
  do {
    try print(foo(a: 1))
  } catch let err as IOError {
    print("IO Error: \(err)")
  } catch {
    print("Unexpected Error")
  }
}
复制代码

那么细的细节编译器可不知道,不写确定是编译不过的,由于函数签名只约定了这个函数会不会 throws,并无约定 throws 的具体类型是什么。尽管看起来粗糙,但实际对于二进制兼容是有帮助的。例如,增长、减小或者改变一种抛出的错误不会形成源代码兼容性被破坏。也能够用 is 来匹配:code

func myFunc() {
  do {
    try print(foo(a: 1))
  } catch is IOError {
    print("Caught IO Error")
  } catch {
    print("Unexpected Error")
  }
}
复制代码

咱们逐渐来到“模式匹配”(pattern matching)的领域,来看这个例子:cdn

func myFunc() {
  do {
    try print(foo(a: 1))
  } catch IOError.diskError(let code) {
    print("Caught error:\(code)")
  } catch IOError.networkError(let code) {
    print("Caught error:\(code)")
  } catch {
    print("Unexpected Error")
  }
}
复制代码

这个例子展现了单模式子句的局限性。尽管绑定的是同一类型的 code,可是必须写成两个catch子句,而且重复处理逻辑。在 Swift 5.3 以前,要解决这个问题还得借助 switch,一个 case 语句能够多模式匹配,惟一的要求是绑定的值必须名字相同、类型相同、而且出如今每个pattern中。编译器

func myFunc() {
  do {
    try print(foo(a: 1))
  } catch let err as IOError {
    switch err {
    case .diskError(let code), .networkError(let code):
      print("Caught error:\(code)")
    }
  } catch {
    print("Unexpected Error")
  }
}

复制代码

不用我多说,这样的问题仍是丑。string

多模式 catch 子句

有了多模式匹配后,明显漂亮了不少,咱们不须要再借助 switch 写出多模式匹配。

func myFunc() {
  do {
    try print(foo(a: 1))
  } catch IOError.diskError(let code), IOError.networkError(let code){
    print("Caught error:\(code)")
  } catch {
    print("Unexpected Error")
  }
}
复制代码

为了展现多模式匹配的能力,咱们玩得花一点:

enum FooBarError: Error {
  case fooError(first:String, second:Int)
}

func myFunc() {
  do {
    try print(foo(a: 1))
  } catch IOError.diskError(let code) where code % 2 == 1, FooBarError.fooError(_, let code){
    print("Caught error:\(code)")
  } catch {
    print("Unexpected Error")
  }
}
复制代码

这里加上了 where 子句用于筛选条件,而且同时匹配的 Error 换成了一个彻底不一样的类型,可是只要知足绑定的变量名字相同、类型相同、而且出如今每个pattern中,那么就能够经过编译。

不是有绑定才是模式匹配,用 is 的也能够两句并一句了。

func myFunc() {
  do {
    try print(foo(a: 1))
  } catch is IOError, is FooBarError{
    print("Caught known error")
  } catch {
    print("Unexpected Error")
  }
}
复制代码

可是两种 Error 类型能不能并一句呢?

func myFunc() {
  do {
    try print(foo(a: 1))
  } catch let err as IOError, let err as FooBarError{
    print("Caught known error")
  } catch {
    print("Unexpected Error")
  }
}
复制代码

上面的代码不能编译经过。尽管两个 err 名字相同,也都出如今两个pattern中,可是它们的类型不相同,违反了上面提到过的原则。

小结

经过多模式 catch 子句,咱们能够像 switch 语句那样对于 Error 进行多模式匹配,减小重复的处理代码。

多模式匹配时,需保证绑定的变量名字相同、类型相同而且出如今每个模式中。

扫码下方二维码关注“面试官小健”

相关文章
相关标签/搜索