switch 在递归类型中必须是详尽的

Posted

技术标签:

【中文标题】switch 在递归类型中必须是详尽的【英文标题】:switch must be exhaustive in Recursive type 【发布时间】:2019-10-30 16:17:23 【问题描述】:
enum Tree<Element: Comparable> 
  case empty
  indirect case node(Tree<Element>, Element, Tree<Element>)

  func forEach(withLooping fn: (Element) -> Void) 
    var stack = [self]
    while !stack.isEmpty 
      let current = stack.popLast()
      switch current 
      case .empty: break
      case let .node(left, value, right):
        fn(value)
        stack.append(left)
        stack.append(right)
      case .none: // !!!
        break
      
    
  

Xcode 强制我添加.none 大小写,但.none 不是Tree 的构造函数

xxx.swift:9:7: error: switch must be exhaustive
      switch current 
      ^
xxx.swift:9:7: note: add missing case: '.none'
      switch current 

为什么?

【问题讨论】:

popLast() 返回Optional&lt;Tree&gt;,它有.none 大小写。 【参考方案1】:

问题不在于枚举是递归的,而是popLast() 方法返回一个可选(如果数组为空,则为nil)。因此current 的可能情况是

case .some(.empty):
case .some(.node(left, value, right)):
case .none: // Or equivalently: case nil:

从 Swift 5.1 开始,枚举案例可以与非可选枚举模式匹配(比较 SR-7799),因此这可以简化为

case .empty:
case .node(left, value, right):
case .none: // Or equivalently: case nil:

这解释了编译器错误和修复它。 但是,nil 的情况不会发生,因为您检查该数组是否为空。以下是三种可能的解决方案:

由于您已经检查堆栈不为空,您可以安全地强制解包

while !stack.isEmpty 
  let current = stack.popLast()!
  switch current 
     // ...

请改用removeLast()。此方法预计数组不为空,并返回一个(非可选的)数组元素:

while !stack.isEmpty 
  let current = stack.removeLast()
  switch current 
     // ...

在 while 条件中使用 popLast() 和可选绑定:

while let current = stack.popLast() 
  switch current 
     // ...

【讨论】:

以上是关于switch 在递归类型中必须是详尽的的主要内容,如果未能解决你的问题,请参考以下文章

Switch 语句必须详尽 - Xcode 错误?

Swift中开关盒的详尽条件

10 - 递归

可变参数递归

可变参数递归

C# REST API,模型的子节点应该是同一类型,并且路由必须递归定义