Interface vazia: any e type assertions
1. O que é a interface vazia (interface{} / any)
Em Go, uma interface vazia é uma interface que não declara nenhum método. Isso significa que todos os tipos da linguagem implementam essa interface implicitamente. Antes do Go 1.18, escrevíamos interface{}; a partir dessa versão, o alias any foi introduzido como uma forma mais legível e idiomática.
var x any // equivalente a var x interface{}
x = 42
x = "hello"
x = 3.14
x = struct{ Name string }{"Alice"}
O código acima compila sem erros porque any aceita qualquer valor. Essa característica torna a interface vazia extremamente flexível, mas também exige cuidado: o compilador não pode mais verificar os tipos em tempo de compilação.
2. Por que usar a interface vazia?
A interface vazia é útil em cenários que exigem máxima flexibilidade:
- APIs genéricas:
fmt.Printlnaceitaanypara imprimir qualquer valor. - Serialização:
json.Marshalejson.Unmarshaltrabalham comany. - Containers heterogêneos: slices e maps que armazenam tipos mistos.
func printAny(v any) {
fmt.Printf("Valor: %v, Tipo: %T\n", v, v)
}
func main() {
printAny(100)
printAny("texto")
printAny([]int{1, 2, 3})
// Slice heterogêneo
misturado := []any{42, "golang", 3.14, true}
fmt.Println(misturado)
}
Limitação importante: ao usar any, perdemos a segurança de tipo em tempo de compilação. Erros só aparecerão em execução, o que pode levar a panics inesperados.
3. Type assertions: extraindo o tipo concreto
Type assertion é o mecanismo para recuperar o tipo concreto de um valor armazenado em uma interface vazia.
Sintaxe básica (propensa a panic)
var v any = "Go é fantástico"
s := v.(string) // funciona
n := v.(int) // panic: interface conversion: interface {} is string, not int
Forma segura com dois retornos
var v any = "Go é fantástico"
if s, ok := v.(string); ok {
fmt.Println("É uma string:", s)
} else {
fmt.Println("Não é uma string")
}
if n, ok := v.(int); ok {
fmt.Println("É um int:", n)
} else {
fmt.Println("Não é um int")
}
Sempre prefira a forma segura com ok em código de produção.
Exemplo prático: função que processa um any
func processValue(v any) {
switch {
case v == nil:
fmt.Println("valor nulo")
default:
if s, ok := v.(string); ok {
fmt.Println("String em maiúsculas:", strings.ToUpper(s))
} else if i, ok := v.(int); ok {
fmt.Println("Inteiro ao quadrado:", i*i)
} else {
fmt.Printf("Tipo desconhecido: %T\n", v)
}
}
}
4. Type switches: inspecionando múltiplos tipos
Type switch é uma construção mais elegante para testar múltiplos tipos possíveis:
func describe(v any) {
switch val := v.(type) {
case nil:
fmt.Println("nil")
case string:
fmt.Printf("string de tamanho %d: %s\n", len(val), val)
case int:
fmt.Printf("int: %d\n", val)
case float64:
fmt.Printf("float64: %f\n", val)
case bool:
fmt.Printf("bool: %v\n", val)
default:
fmt.Printf("tipo inesperado: %T\n", val)
}
}
func main() {
describe(42)
describe("Olá")
describe(3.14)
describe(true)
describe(nil)
describe([]byte{1, 2, 3})
}
A variável val dentro de cada case já possui o tipo correspondente, eliminando a necessidade de type assertions adicionais.
5. Cuidados e boas práticas com type assertions
Sempre usar a forma segura
// ❌ Propenso a panic
func extrairString(v any) string {
return v.(string)
}
// ✅ Seguro
func extrairStringSeguro(v any) (string, bool) {
s, ok := v.(string)
return s, ok
}
Evitar type assertions excessivas
Se você precisa fazer múltiplas type assertions em sequência, considere usar type switch ou, melhor ainda, redefinir sua API com interfaces mais específicas.
// ❌ Ruim: múltiplas assertions
func process(v any) {
if s, ok := v.(string); ok { /* ... */ }
if i, ok := v.(int); ok { /* ... */ }
if f, ok := v.(float64); ok { /* ... */ }
}
// ✅ Melhor: type switch
func process(v any) {
switch v.(type) {
case string: /* ... */
case int: /* ... */
case float64: /* ... */
}
}
// ✅ Ideal: interface específica
type Stringer interface {
String() string
}
6. Relação com outros temas da série
Receivers por valor vs. por ponteiro
any pode conter tanto valores quanto ponteiros. Isso afeta o comportamento de type assertions:
var v any = &Person{Name: "Alice"}
p, ok := v.(*Person) // ok, assertion em ponteiro
Contraste com interfaces tipadas
type Animal interface {
Som() string
}
// any não oferece contrato comportamental
var a any = "cachorro"
// a.Som() // erro de compilação: any não tem método Som
// Interface tipada oferece garantias
var animal Animal = Cachorro{}
animal.Som() // funciona
Erros em Go
error é uma interface específica com um único método (Error() string), não um any:
var err error = fmt.Errorf("algo deu errado")
var v any = err
e, ok := v.(error) // true, pois error implementa a interface
7. Casos de uso reais e alternativas modernas
Uso em bibliotecas padrão
// encoding/json
var data any
json.Unmarshal([]byte(`{"nome":"Alice","idade":30}`), &data)
m := data.(map[string]any)
fmt.Println(m["nome"]) // Alice
// database/sql
var result any
db.QueryRow("SELECT name FROM users WHERE id=?", 1).Scan(&result)
Alternativa moderna: generics (Go 1.18+)
Com generics, muitos usos de any podem ser substituídos por código mais seguro:
// Antes: any
func SomaInteiros(vals []any) int {
total := 0
for _, v := range vals {
if i, ok := v.(int); ok {
total += i
}
}
return total
}
// Depois: generics
func Soma[T int | float64](vals []T) T {
var total T
for _, v := range vals {
total += v
}
return total
}
Quando ainda faz sentido usar any
- APIs que realmente precisam aceitar qualquer tipo (ex:
fmt.Println,json.Marshal) - Interfaces com sistemas externos onde o tipo só é conhecido em tempo de execução
- Código legado que ainda não foi migrado para generics
Em projetos novos, prefira generics sempre que possível, mas mantenha any como ferramenta para casos legítimos de heterogeneidade dinâmica.
Referências
- Documentação oficial: Type assertions — Tour interativo explicando type assertions com exemplos práticos
- Effective Go: Type switches — Seção do guia oficial sobre type switches e boas práticas
- Go 1.18 Release Notes: any alias — Introdução do alias
anyparainterface{}na versão 1.18 - Go by Example: Type Assertions — Exemplos concisos e comentados de type assertions e type switches
- The Go Blog: Generics in Go — Artigo oficial sobre generics, comparando com o uso tradicional de
interface{} - Go Wiki: Interface Slicing and Type Assertions — Discussão sobre slices de interfaces e boas práticas com type assertions