A História de como eu ajudei a aprimorar* uma das linguagens de programação mais famosas do mundo: a linguagem Go.

Published: (December 18, 2025 at 03:41 PM EST)
7 min read
Source: Dev.to

Source: Dev.to

Introdução

Tudo começou a cerca de 1 mês, quando eu decidi experimentar a chamada linguagem Go de que tanto falavam. Diziam que ela tinha excelente performance, era moderna, era segura e era tudo isso e mais um pouco. Fiquei tentado então a experimentar essa linguagem ao menos uma vez. Mas, como sempre, na vida de um desenvolvedor as coisas quase nunca funcionam bonitinho e de primeira kkk.

Eu, como alguns por aqui já sabem, gosto muito de C++ e a maior parte do meu desenvolvimento em projetos pessoais ocorre nele. Uma coisa que eu acabei me acostumando ao longo desses anos desenvolvendo em C++ foi o fato de que, nele, os identificadores de tipos e os identificadores de variáveis existem em espaços de nomes distintos um do outro. Esse não era o caso em Go: lá, os identificadores existem todos no mesmo espaço de nomes.

Mas o que é um identificador? E um espaço de nomes? 🤔

De forma simplificada, um identificador é basicamente o nome que se dá a qualquer tipo, variável ou função. Por exemplo, se você declara uma variável do tipo string com o nome senhaDoUsuario, tanto o nome do tipo (string) quanto o nome da própria variável (senhaDoUsuario) são considerados identificadores.

O espaço de nomes, intuitivamente, pode ser entendido como a região onde os nomes dos tipos e das variáveis ficam armazenados (como se fosse um listão, sabe?).

// Exemplo em TypeScript
let nomeDoUsuario: string = '...';
//  ┬────────────  ┬─────
//  │              ╰────▶ Identificador
//  ╰───────────────────▶ Identificador

Em C++ (e também em outras linguagens), os identificadores que referem a tipos e os que referem a variáveis existem em espaços de nomes separados (duas listas distintas), de forma a não conflitarem entre si caso tenham o mesmo valor. Ou seja, você pode criar um tipo com o nome pessoa e uma variável também chamada pessoa ao mesmo tempo, sem nenhum problema; o compilador entenderá, contextualmente, quem é o tipo e quem é a variável.

O que acontece em Go?

Como mencionado anteriormente, na linguagem Go todos os identificadores existem no mesmo espaço de nomes (em teoria de linguagens de programação, isso se chama flat namespace). Isso significa que eles podem conflitar entre si. Não é exatamente um conflito que impede a compilação, mas sim um caso de shadowing: se você declarar um tipo com o nome pessoa e, logo abaixo, declarar uma variável com o mesmo nome, o tipo pessoa deixará de ser referenciável nas linhas seguintes, sendo “ofuscado” pela variável.

Esse detalhe foi a raiz do meu sofrimento.

Um exemplo prático: manipulação de arquivos

Quando eu fui experimentar a linguagem pela primeira vez, queria fazer algo simples porém não trivial: manipulação de arquivos de texto. O interessante do Go é que o tratamento de erros é feito pelos valores de retorno; todos os erros que possam acontecer são explicitamente retornados pela função como se fossem valores quaisquer (ao contrário de Java, onde exceções são lançadas). Isso resulta num padrão onde você tem o valor real da função acompanhado do possível erro emitido durante sua execução.

// arquivo (simplificado): example.go
value, err := foo()
if err != nil {
    fmt.Println(err)
}
fmt.Println(value)

Isso por si só é tranquilo, sendo até preferível se você vem de uma linguagem como C e não curte muito a ideia de exceções. O problema surge quando consideramos o fato mencionado anteriormente: todos os identificadores existem no mesmo espaço de nomes.

Imagine agora que você queira explicitar os tipos dos valores retornados pela função foo.

// arquivo (simplificado): example.go
var (
    value int
    err   error
)

value, err = foo()
if err != nil {
    fmt.Println(err)
    return
}
fmt.Println(value)

Tudo parece normal, certo? O código compila bonitinho, tudo certo, tudo uma maravilha. Mas aí é que se esconde o motivo que motivou este post.

O conflito entre variável e tipo error

Perceba que eu utilizei o mesmo nome tanto para a variável err quanto para o tipo error. (No exemplo acima usei err apenas para evitar a sombra imediata, mas o problema persiste se usarmos o mesmo nome.) Leve em consideração agora a seguinte situação:

// arquivo (simplificado): example.go
var (
    value int
    error *error // <-- variável chamada "error"
)

value, error = foo()
if error != nil {
    fmt.Println(error.Error())
    return
}
fmt.Println(value)

var (
    anotherValue int
    anotherError error // <-- tentativa de declarar variável do tipo "error"
)

anotherValue, anotherError = foo()
if anotherError != nil {
    fmt.Println(anotherError.Error())
    return
}
fmt.Println(anotherValue)

O que acontece?

Ao tentar compilar esse código, você receberá a seguinte mensagem de erro:

./example.go:28:16: error is not a type

Por quê?

Em Go, identificadores de variáveis e identificadores de tipos compartilham o mesmo espaço de nomes. No trecho acima, a declaração

error *error

cria uma variável chamada error. A partir desse ponto, toda referência ao identificador error será interpretada como variável, não como o tipo predefinido error. Quando o compilador encontra a linha

anotherError error

ele tenta usar error como um tipo, mas já está “sombreado” pela variável error. Assim, ele relata que error não é um tipo.

Conclusão

  • Em C++ (e em várias outras linguagens) tipos e variáveis vivem em espaços de nomes separados, permitindo que um mesmo nome seja usado simultaneamente para ambos.
  • Em Go todos os identificadores compartilham um flat namespace.
  • Quando você declara uma variável com o mesmo nome de um tipo (por exemplo, error), a variável sombreia o tipo nas linhas seguintes, gerando erros de compilação como “identifier is not a type”.
  • A solução mais simples é evitar nomes que colidam com tipos predefinidos (como error, int, string, etc.) ou usar nomes diferentes para variáveis e tipos.

Esse detalhe pode parecer pequeno, mas pode consumir horas de depuração, como aconteceu comigo. Fique atento ao namespace plano de Go e escolha nomes de variáveis que não conflitem com tipos existentes!

Considerado semânticamente inválido

Dai que entra a minha parte nessa história.

Hoje, depois de muito tempo sem encostar nessa linguagem devido à frustração resultante desse ocorrido, me deparei com um post no LinkedIn comentando sobre ela novamente. No momento em que eu me vi lendo a postagem, eu me recordei dessa experiência desagradável que tive em primeira mão com Go. Dai eu decidi fazer o seguinte: por que não abrir um issue no repositório da linguagem sugerindo uma possível melhoria nisso?

A ideia era clara: ao invés de retornar isso

./example.go:28:16: error is not a type

por que não retornar algo como isso?

./example.go:28:16: error refers to a variable, but is being used as type here.
./example.go:16:37: error was re‑declared here as a variable (originally declared as a type at example.go:5:37).

Seria muito melhor! Evitaria que outras pessoas no futuro perdessem tempo tentando descobrir o significado por trás daquela mensagem misteriosa. Pelo menos era o que eu pensava.

Entretanto, quando entrei no repositório da linguagem, vi a quantidade imensa de issues abertas no projeto (mais de cinco mil) e confesso que deu uma desanimada. Com tudo isso aberto, seria muito improvável que o meu, de um maluco aleatório que nunca contribuiu antes para essa linguagem, fosse sequer visto por alguém.

Mas apesar de tudo, eu ainda assim fui lá e abri.

E, depois de alguns minutos… não é que responderam!? Melhor ainda, eles aceitaram a minha sugestão! Fiquei bem feliz em ver que realmente levaram em consideração a crítica que fiz e aceitaram colocar no backlog do projeto como meta de melhoria para a próxima release oficial. Então, se você, em algum momento distante, utilizar essa linguagem e se deparar novamente com a mesma situação… saiba que ela já foi bem pior!

É isso! Talvez agora eu possa dizer (e colocar no currículo 👀) que, de certa forma, contribuí para uma das linguagens mais famosas do mundo atualmente! Kkkkkk.

Talvez seja esticar um pouco demais a noção de contribuição, entretanto, convenhamos, o que vale é a intenção! 😁

Back to Blog

Related posts

Read more »