Concise pattern matching for enum cases with associated values?

Apologies if this has been asked before, but I couldn't find any related threads.

~= works for simple cases like .foo , but for cases with associated values like .bar , it requires more verbose handling:

enum E: Equatable {
    case foo
    case bar(Int)
}

let es: [E] = (0..<3).map { _ in Bool.random() ? .foo : .bar(123) }

// For `foo` the following reads quite nice:
if es.contains(where: { .foo ~= $0 }) {
    print("es contains at least one foo")
}

// For `.bar`, we need this (unless I'm missing something):
if es.contains(where: { if case .bar = $0 { true } else { false } }) {
    print("es contains at least one bar")
}

Is there a more concise way to handle this for .bar ?

If not, have there been any proposals in Swift Evolution to improve this?

I've definitely seen this discussed before. iirc one proposed solution was this:

enum E: Equatable {
  case foo
  case bar(Int)

  enum Discriminator {
    case foo
    case bar

    static func ~= (lhs: Discriminator, rhs: E) -> Bool {
      switch (lhs, rhs) {
      case (.foo, .foo): true
      case (.bar, .bar(_)): true
      default: false
      }
    }
  }
}

Then, .bar ~= $0 should work, if it infers the overload that takes E.Discriminator for the LHS.

That seems more like a cumbersome workaround than a solution :slight_smile:.
Another way to work around it might be for example:

extension E {
  var isBar: Bool { if case .bar = self { true } else { false } }
}

I wonder what the reasons are behind not allowing ~= to be used for matching cases with omitted associated values, given that it's allowed in an if/guard case and in a switch:

switch es[0] {
  case .foo: break
  case .bar: break // This is fine even though we choose to omit the associated value.
}

Maybe it has to do with .bar not being an actual case value (without its associated value), i.e. incomplete case values like .bar is perhaps only allowed after case in the grammar.

Not the answer you are looking for but swift-case-paths – Swift Package Index it's always in my tool-belt as soon as I need to do things with enum. Maybe is worth your consideration.

1 Like