Erro do compilador Swift: o caso de enum possui uma única tupla como valor associado, mas existem vários padrões aqui

12

Construindo um projeto no Xcode 11.4 beta 3, estou recebendo este erro do Swift Compiler em uma enumeração:

O caso enum possui uma única tupla como valor associado, mas existem vários padrões aqui, tuplicando implicitamente os padrões e tentando correspondê-los

Código fonte:

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

Resulté uma enumeração genérica com valores associados para .errore .value. Nesse caso, o valor associado é um tupple.

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

Não se lembre de ter visto esse erro antes e a pesquisa não resultou em nenhum resultado. Alguma ideia?

Eneko Alonso
fonte
11
Eu atualizei a pergunta, desculpe por deixar isso de fora
Eneko Alonso
Não há necessidade de reinventar a roda Resultado; isso já existe. developer.apple.com/documentation/swift/result
matt
Além disso, ainda não existe o Xcode 11.4 beta 4.
matt
Meu mal, eu quis dizer Xcode 11.4 beta 3. No que diz respeito Result, eu concordo, é o código antigo que antecede Swift.Result. Isso não tem nada a ver com o problema.
Eneko Alonso
11
Concordo plenamente, estou apenas tentando limpar a questão. Você levanta um bom argumento aqui e esta é a nossa chance de documentar as abordagens corretas para os outros encontrarem.
matt

Respostas:

14

Descobri que você também pode silenciar esse erro tratando o valor associado mais como uma tupla, envolvendo-o em um conjunto extra de colchetes:

switch result {
case .error(let err):
    //
case .value((let staff, let locations)):  
    //
}
Wernzy
fonte
11
Isso é legal, eu gosto, obrigado.
Eneko Alonso
2
Considere mover a letsaída se quiser vincular tudo: case let .value( (staff, locations) ):e case .value( let (staff, locations) ):ambos compilarem. Escolha o seu favorito!
Jessy
11
Super menor, mas eu discordo estilisticamente do comentário acima sobre vincular tudo com um único let. Ter o let à esquerda da coisa vinculada é mais fácil de ler e entender rapidamente o que está vinculado. Caso contrário, você precisa extrapolar mentalmente o que o let é obrigatório. As diretrizes de codificação do Google para o swift também desaconselham a permissão em cascata única: google.github.io/swift/#pattern-matching
ToddH
2
Diretrizes do "Google": /
Gee.E
9

Ok, entendi. Parece que enumcom valores associados, em que o tipo de valor é um tupple, não pode mais ser correspondido em uma instrução switch como esta:

// 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):  
    //
}

Solução

Os valores do tupple precisam ser extraídos manualmente no 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
    // 
}
Eneko Alonso
fonte
Essa é certamente uma solução.
matt
3

Este é um problema conhecido: https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_release_notes

O código que depende do compilador que tupla automaticamente um padrão pode levar a um erro do compilador ao atualizar para o Xcode 11.4, mesmo que o código tenha sido compilado anteriormente. (58425942)

Por exemplo, deixar de fora parênteses ao ativar um Opcional de um tipo de tupla causa um erro do compilador:

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
...

}

Solução alternativa : adicione parênteses extras para tuplicar explicitamente o padrão:

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

}

bolinhalouise
fonte
Obrigado pelas informações adicionais e link para as notas de versão. Eu senti falta disso.
Eneko Alonso
0

Se me permite, gostaria de adicionar uma resposta para a if caseversão também.

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

e, claro, ignorando o caso:

if case let .value(staff, _) = result {
    // Do something
}
Paul Peelen
fonte