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?


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

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

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

}


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
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.