Подтвердить что ты не робот

Исчерпывающее состояние корпуса переключателя в Swift

Apple documentation говорит

Каждый оператор switch должен быть исчерпывающим. То есть, всевозможные значение рассматриваемого типа должно соответствовать одному из корпусов выключателей.

Итак, в новом Xcode я разместил код, подобный этому

println(UInt16.min); // Output : '0'
println(UInt16.max); // Output : '65535'

var quantity : UInt16 = 10;

switch quantity {
case 0...65535: //OR case UInt16.min...UInt16.max:
    println();
default:
    println();
}

Теперь, если я удаляю раздел по умолчанию, я получаю ошибку компиляции, показывающую, что коммутатор должен быть исчерпывающим

Итак, мой вопрос касается случая, который я упомянул как case 0...65535:, чтобы я не упомянул все значения case для UInt16? Но все же я получаю ошибку?? Почему я получаю эту ошибку, пропустил ли я что-то?

4b9b3361

Ответ 1

Swift только по-настоящему проверяет, что блок switch является исчерпывающим при работе с типами enum. Даже для включения Bool требуется блок default в дополнение к true и false:

var b = true
switch b {
case true:  println("true")
case false: println("false")
}
// error: switch must be exhaustive, consider adding a default clause

С enum, однако, компилятор с удовольствием рассмотрит только два случая:

enum MyBool {
    case True
    case False
}

var b = MyBool.True
switch b {
case .True:  println("true")
case .False: println("false")
}

Если вам нужно включить блок default для компилятора, но у него нет ничего для этого, ключевое слово break будет полезно:

var b = true
switch b {
case true:  println("true")
case false: println("false")
default: break
}

Ответ 2

Часть того, почему вы видите эту ошибку, потому что компилятор не может проверить, что коммутатор является исчерпывающим, без запуска кода. Выражение 0...65535 создает структуру ClosedInterval, и когда оператор switch выполняется, он должен запрашивать эту структуру, если значение quantity находится в интервале. Там есть место для изменения во время выполнения, поэтому компилятор не может проверить его во время компиляции. (См. Проблема с остановкой.)

В общем случае компилятор не может обнаружить исчерпывающий переключатель для целочисленных значений - даже если вы добавляете конкретные случаи для каждого целочисленного значения (case 0: ... case 1: ... ... case 65535:), он не знает, что ваш коммутатор является исчерпывающим. (Теоретически это могло бы: рассмотреть подачу запроса функции об этом, если это то, что вы хотели бы видеть.)

В сущности, есть два сценария, в которых Swift может обнаружить полноту и позволить вам опустить предложение default: перечисления и привязка значений в кортежах. Ответ @NateCook охватывает перечисления - если вы включаете значение перечисления и имеете case в switch для каждого case в перечислении, вам не нужно default. Вы также не нуждаетесь в метке default, если вы включаете кортеж и связываете все возможные комбинации значений, как показано в книге Swift

switch anotherPoint {
case (let x, 0):
    println("on the x-axis with an x value of \(x)")
case (0, let y):
    println("on the y-axis with a y value of \(y)")
case let (x, y):
    println("somewhere else at (\(x), \(y))")
}

Вы можете обобщить это правило как "если система типов знает о возможных значениях вашего типа, она может обнаружить полноту switch", но тот факт, что существует уровень, на котором система типов не знает диапазон возможных (например) UInt32 значений является своего рода расщепляющими волосками...

Ответ 3

Swift 4.1. Либо вам нужно указать все случаи, либо просто включить блок по умолчанию в операторе switch.

Ответ 4

(Начиная с Swift 4.2 и, возможно, ранее): у меня есть вспомогательная функция, которая преобразует Bool? в selectedSegmentIndex для UISegmentedControl с 2 сегментами. Если значение равно nil ни один сегмент не должен быть выбран. Моя функция использует переключатель, который возвращает соответствующий индекс сегмента для значений true или false, и использует его для явного тестирования nil и удовлетворения потребности компилятора в том, что он является исчерпывающим:

case nil:
    fallthrough
default:
    return UISegmentedControl.noSegment

Технически, case nil: fallthrough не требуется, потому что будет достаточно default: но этот синтаксис может быть полезен, если вы хотите явно проверить значение, чтобы сделать код более самодокументированным, или, возможно, в другой ситуации.