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 進行多模式匹配,減小重複的處理代碼。

多模式匹配時,需保證綁定的變量名字相同、類型相同而且出如今每個模式中。

掃碼下方二維碼關注「面試官小健」

相關文章
相關標籤/搜索