Difference between switch cases “@unknown default” and “default” in Swift 5

From SE-0192: Handling Future Enum Cases (emphasis mine):

When switching over a non-frozen enum, the switch statement that
matches against it must include a catch-all case (usually default or
an “ignore” _ pattern).

switch excuse {
case .eatenByPet:
  // …
case .thoughtItWasDueNextWeek:
  // …
}

Failure to do so will produce a warning in Swift 5. A program will
trap at run time if an unknown enum case is actually encountered.

All other uses of enums (if case, creation, accessing members, etc)
do not change. Only the exhaustiveness checking of switches is
affected by the frozen/non-frozen distinction. Non-exhaustive switches
over frozen enums (and boolean values) will continue to be invalid in
all language modes.

Here’s a more complicated example:

switch (excuse, notifiedTeacherBeforeDeadline) {
case (.eatenByPet, true):
  // …
case (.thoughtItWasDueNextWeek, true):
  // …
case (_, false):
  // …
}

This switch handles all known patterns, but still doesn’t account for
the possibility of a new enum case when the second tuple element is
true. This should result in a warning in Swift 5, like the first
example.

@unknown

The downside of using a default case is that the compiler can no
longer alert a developer that a particular enum has elements that
aren’t explicitly handled in the switch. To remedy this, switch
cases will gain a new attribute, @unknown.

switch excuse {
case .eatenByPet:
  // …
case .thoughtItWasDueNextWeek:
  // …
@unknown default:
  // …
}

Like the regular default, @unknown default matches any value; it is
a “catch-all” case. However, the compiler will produce a warning if
all known elements of the enum have not already been matched. This is
a warning rather than an error so that adding new elements to the enum
remains a source-compatible change. (This is also why @unknown default
matches any value rather than just those not seen at compile-time.)

@unknown may only be applied to default or a case consisting of the
single pattern _. Even in the latter case, @unknown must be used
with the last case in a switch. This restriction is discussed further
in the “unknown patterns” section under “Future directions”.

The compiler will warn if all enums in the pattern being matched by
@unknown are explicitly annotated as frozen, or if there are no enums
in the pattern at all. This is a warning rather than an error so that
annotating an enum as frozen remains a source-compatible change. If
the pattern contains any enums that are implicitly frozen (i.e.
because it is a user-defined Swift enum), @unknown is permitted, in
order to make it easier to adapt to newly-added cases.

@unknown has a downside that it is not testable, since there is
no way to create an enum value that does not match any known cases,
and there wouldn’t be a safe way to use it if there was one. However,
combining @unknown with other cases using fallthrough can get the
effect of following another case’s behavior while still getting
compiler warnings for new cases.

switch excuse {
case .eatenByPet:
  showCutePicturesOfPet()

case .thoughtItWasDueNextWeek:
  fallthrough
@unknown default:
  askForDueDateExtension()
}

Leave a Comment