Introdução
Go tem uma proposta clara: simplicidade, legibilidade e segurança. Mas por trás dessa simplicidade, existem nuances que podem gerar comportamentos inesperados — principalmente quando trabalhamos com interfaces e nil.
Se você já escreveu algo como if err != nil e tomou um susto porque entrou no bloco mesmo com um erro aparentemente “nulo”, esse post é pra você.
Como funciona uma interface em Go
Internamente, uma interface em Go é representada como uma estrutura com dois campos invisíveis:
Tipo dinâmico (
type): qual é o tipo do valor armazenado.Valor dinâmico (
value): o valor em si.
Representação conceitual:
interface {
type: *MyStruct
value: nil
}Se qualquer um desses campos estiver preenchido, a interface não será igual a nil.
Caso clássico: interface com ponteiro nil
Vamos simular um caso simples, que passa despercebido por muitos:
Saída:
Isso vale para qualquer interface
var ptr *int = nil
var i interface{} = ptr
fmt.Println(i == nil) // false!
Por que isso é um problema real?
Porque essa armadilha aparece em situações comuns, como:
Retorno de erros (
error)Implementações com ponteiros (
io.Reader,json.Marshaler, etc.)Middlewares ou wrappers de bibliotecas
Código legível que não se comporta como esperado
E o pior: esse erro pode passar batido em testes se o nil for ignorado.
Como evitar esse comportamento
1. Sempre retorne nil puro se for pra indicar ausência
Errado:
func getErr() error {
var e *MyError = nil
return e
}
Certo:
2. Cuidado ao inicializar variáveis interface{} com valores nulos
Evite:
var x *Something = nil
var i interface{} = x
Prefira:
var i interface{} = nil
Ou verifique explicitamente o conteúdo:
if ptr, ok := i.(*Something); ok && ptr == nil {
fmt.Println("i contém um ponteiro nil")
}
3. Para funções que retornam interface{} ou error, use tipos concretos ou nil
Evite retornos assim:
func busca() interface{} {
var obj *Coisa = nil
return obj // armadilha
}Prefira:
func busca() *Coisa {
return nil // retorno claro, sem interface
}
4. Ferramentas de análise estática podem salvar seu dia
Use staticcheck — ele detecta esse tipo de padrão e avisa com mensagens como:
SA5009: return of a nil value of type *T in an interface
Como debugar interfaces suspeitas
Às vezes, só de olhar pro valor você não entende o que está acontecendo. Use esse padrão pra inspecionar:
fmt.Printf("Tipo: %T | Valor: %#v\n", minhaInterface, minhaInterface)
Utilitário de debug:
func InterfaceInfo(i interface{}) {
if i == nil {
fmt.Println("Interface é nil")
return
}
fmt.Printf("Interface não é nil\n Tipo: %T\n Valor: %#v\n", i, i)
}
Entendendo em profundidade: interface{} vs tipos concretos
interface{} == nil
Só será verdadeiro se não houver tipo nem valor associado.
var i interface{} = nil // ok
if i == nil { // true
interface{} contendo (*T)(nil)
Será diferente de nil:
Quando esse bug costuma aparecer na prática?
Em middlewares de erro que retornam ponteiros
Em testes com
assert.Nil(err)Em código legível que não faz o que promete
Em wrappers que embalam valores sem perceber
Conclusão
O sistema de tipos de Go é simples e poderoso, mas como tudo que lida com ponteiros e abstração, precisa de atenção. O nil em interfaces é uma das armadilhas mais traiçoeiras e silenciosas da linguagem.
Regras de ouro:
Interface só é
nilse tipo e valor forem nilEvite retornar ponteiros
nilembrulhados em interfacesUse ferramentas como
staticcheckSempre teste explicitamente os tipos se necessário
Por hoje é só pessoal. Keep on coding. E lembrem-se: Don’t believe the hype






