Ошибка компилятора Swift: регистр enum имеет один кортеж в качестве связанного значения, но здесь есть несколько шаблонов

12

При создании проекта в Xcode 11.4 beta 3 я получаю следующую ошибку Swift Compiler:

Случай enum имеет один кортеж в качестве связанного значения, но здесь есть несколько шаблонов, неявно объединяющих шаблоны и пытающихся сопоставить их вместо

Исходный код:

switch result {
case .error(let err):
    //
case .value(let staff, let locations):  // <-- error on this line
    //
}

Resultявляется общим перечислением со связанными значениями для .errorи .value. В этом случае ассоциированное значение является набором.

public enum Result<T> {
    case value(T)
    case error(Error)
}

Не забывайте видеть эту ошибку раньше, и поиск ее не дал никаких результатов. Любые идеи?

Энеко Алонсо
источник
1
Я обновил вопрос, извините за это
Энеко Алонсо
Нет необходимости заново изобретать колесо результатов; это уже существует. developer.apple.com/documentation/swift/result
матовый
Также нет Xcode 11.4 beta 4 (пока).
Мат
Мой плохой, я имел в виду Xcode 11.4 beta 3. Что касается Result, я согласен, это старый код, который предшествует Swift.Result. Это не имеет ничего общего с проблемой, хотя.
Энеко Алонсо
1
Я полностью согласен, я просто пытаюсь решить вопрос. Вы подняли здесь хороший вопрос, и это наш шанс задокументировать правильные подходы, которые найдут другие.
Мат

Ответы:

14

Я обнаружил, что вы можете также заставить эту ошибку замолчать, рассматривая ассоциированное значение как кортеж, заключив его в дополнительный набор скобок:

switch result {
case .error(let err):
    //
case .value((let staff, let locations)):  
    //
}
Wernzy
источник
1
Это хорошо, мне это нравится, спасибо.
Энеко Алонсо
2
Подумайте об удалении let, если вы собираетесь связать все, хотя: case let .value( (staff, locations) ):и case .value( let (staff, locations) ):оба компилировать. Выбери свой любимый!
Джесси
1
Супер минор, но я стилистически не согласен с приведенным выше комментарием о связывании всего с одним let. Обозначение слева от того, что связано, легче для чтения и быстрого понимания того, что связано. В противном случае вам нужно мысленно экстраполировать то, что связывает let. Рекомендации Google по кодированию для swift также не рекомендуют использовать один каскад: google.github.io/swift/#pattern-matching
ToddH
2
Рекомендации "Google": /
Gee.E
9

Ок, разобрался. Похоже, enumсо связанными значениями, где тип значения является набором, больше нельзя сопоставлять с оператором switch следующим образом:

// Works on Xcode 11.3.1, yields error on 11.4 (Swift 5.2)
switch result {
case .error(let err):
    //
case .value(let staff, let locations):  
    //
}

Решение

Значения из tupple должны быть извлечены вручную в Xcode 11.4 (Swift 5.2):

// Works on Xcode 11.4
switch result {
case .error(let err):
    //
case .value(let tupple):  
    let (staff, locations) = tupple
    // 
}
Энеко Алонсо
источник
Это, безусловно, одно из решений.
Мат
3

Это известная проблема: https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_release_notes

Код, который полагается на компилятор, автоматически изменяющий шаблон, может привести к ошибке компилятора при обновлении до Xcode 11.4, даже если код скомпилирован ранее. (58425942)

Например, оставление скобок при включении Optional типа кортежа приводит к ошибке компилятора:

switch x { // error: switch must be exhaustive
case .some((let a, let b), let c): // warning: the enum case has a
     // single tuple as an associated value, but there are several
     // patterns here, implicitly tupling the patterns and trying
     // to match that instead
...

}

Обходной путь : Добавьте дополнительные скобки, чтобы явно изменить шаблон:

switch x {
case .some(((let a, let b), let c)): // Notice the extra pair of parentheses.
...

}

bolinhalouise
источник
Спасибо за дополнительную информацию и ссылку на заметки о выпуске. Я пропустил это.
Энеко Алонсо
0

Если можно, я бы хотел добавить ответ и для if caseверсии.

if case let .value(staff, error) = result {
    // Do something
}

и тогда конечно игнорируем регистр:

if case let .value(staff, _) = result {
    // Do something
}
Пол Пилен
источник