Menu

Type assertion em Golang: x.(T), vírgula-ok e type switch

Uma type assertion tira o valor concreto de dentro de uma interface. Veja x.(T), a forma vírgula-ok que nunca causa panic, a assertion para outra interface, type switches e errors.As para erros empacotados.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

x.(T): tirando o valor de dentro de uma interface

Um valor de interface esconde o tipo concreto que guarda. Uma type assertion x.(T) pede esse tipo de volta.

Saída:

gopher 6
0 false
string of length 6

x precisa ter um tipo interface. Fazer assertion sobre um tipo concreto não compila: invalid operation: s (variable of type string) is not an interface.

A forma de um valor causa panic

Quando a assertion está errada, a forma de um único valor causa panic com uma mensagem que nomeia os dois tipos:

Saída:

recovered: interface conversion: interface {} is int, not string

Use a forma de um valor só quando um tipo diferente for um erro de programação no qual você quer que o programa caia. Em todos os outros casos, use vírgula-ok. Em caso de falha, a vírgula-ok devolve o valor zero de T e false.

Fazer assertion em uma interface nil também falha: var v any; v.(string) causa panic com interface conversion: interface {} is nil, not string, e a forma vírgula-ok devolve "", false.

Assertion para outra interface

T pode ser um tipo interface. Nesse caso a assertion dá certo se o valor dinâmico implementar T, e o resultado mantém o mesmo valor dinâmico. É assim que a biblioteca padrão verifica capacidades opcionais.

O io.Copy verifica se a origem implementa io.WriterTo e o usa quando implementa. O fmt verifica Stringer e error. O padrão permite que uma interface pequena continue pequena enquanto quem chama ainda se beneficia de implementações mais ricas.

Type switch

Quando um valor pode ser de um entre vários tipos, um type switch substitui uma cadeia de assertions. x.(type) só é válido dentro de um switch.

Saída:

hi
integer 7
integer 8
3.14
nil
error: boom
unhandled []int

Regras que valem a pena conhecer:

  • Em um case com um único tipo, x tem esse tipo. Em um case que lista vários tipos, e no default, x tem o tipo da interface original (any aqui).
  • Os cases são verificados em ordem. Coloque interfaces mais específicas antes das mais amplas, já que um valor pode satisfazer várias.
  • case nil só casa com uma interface nil, não com uma interface que guarda um ponteiro nil.
  • Não existe fallthrough em um type switch.

errors.As: a assertion para erros empacotados

Erros são muitas vezes empacotados com contexto: fmt.Errorf("load config: %w", err). Uma type assertion direta só olha o erro mais externo e deixa passar o que está dentro. O errors.As percorre a cadeia.

Saída:

type assertion finds it: false
errors.As finds it: open /no/such/file

Use type assertion em erros só quando você sabe que o erro não está empacotado, o que na prática quer dizer quase nunca. A página de erros personalizados cobre errors.As, errors.Is e a definição dos seus próprios tipos de erro.

Assertions, conversões e generics

Você temVocê querUse
intfloat64conversão: float64(n)
any guardando um into intassertion: v.(int)
any com vários tipos possíveisramificar pelo tipotype switch
um error que pode estar empacotadoum tipo de erro específicoerrors.As
uma função que serve para muitos tipossegurança em tempo de compilaçãogenerics

Código cheio de any e type switches costuma ser sinal de que generics ou uma interface adequada expressariam melhor a intenção e pegariam erros em tempo de compilação.

Erros comuns

  • Usar a forma que causa panic em dados não confiáveis. JSON decodificado, valores de map do tipo any, entradas de plugins: sempre vírgula-ok.
  • Fazer assertion para o tipo numérico errado. Números JSON são decodificados em any como float64, então v.(int) falha com eles.
  • Fazer assertion de um tipo valor quando um ponteiro está guardado. Se a interface guarda *User, então v.(User) falha. Faça v.(*User).
  • Type assertions em erros. Use errors.As.

Perguntas frequentes

O que é uma type assertion em Go?

Uma expressão x.(T) em que x é um valor de interface. Se T é um tipo concreto, ela devolve o valor guardado em x como um T. Se T é um tipo interface, ela verifica se o valor guardado também implementa T. A forma de um valor causa panic quando a verificação falha; a forma de dois valores v, ok := x.(T) informa isso em ok.

Como verificar o tipo de um valor de interface em Go?

Use um type switch: switch v := x.(type) { case int: ...; case string: ...; default: ... }. Dentro de cada case, v tem o tipo daquele case. Para um único tipo, a assertion vírgula-ok s, ok := x.(string) é mais curta. fmt.Printf("%T", x) imprime o nome do tipo para depuração.

Qual a diferença entre type assertion e conversão de tipo em Go?

Uma conversão T(x) transforma um valor de um tipo em outro, como float64(n); o compilador decide se ela é permitida, e ela não pode falhar em tempo de execução. Uma assertion x.(T) só funciona em valores de interface e verifica em tempo de execução qual tipo está guardado dentro. Você não pode escrever int(x) para um any que guarda um int; precisa de x.(int).

Devo usar type assertion para verificar tipos de erro?

Não. Use errors.As(err, &target). Uma assertion direta err.(*MyError) só olha o erro mais externo, então falha assim que o erro é empacotado com fmt.Errorf("...: %w", err). O errors.As percorre a cadeia de empacotamento.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR