Go em 2025: a linguagem à beira de uma revolução
\n\nQuando em 2009 Rob Pike, Ken Thompson e Robert Griesemer lançaram o Go, dificilmente alguém imaginava que, 16 anos depois, esta linguagem se tornaria a base do Kubernetes, Docker, Prometheus e milhares de outros projetos. Mas 2025 não é apenas mais um ciclo de evolução. É o ano em que o Go deixa de ser uma "linguagem simples para backend" e se transforma numa plataforma para IA, novos padrões de tratamento de erros e trabalho nativo com iteradores.
\n\nNeste artigo, analisaremos as principais mudanças já anunciadas ou esperadas no Go 1.24–1.25 e as mostraremos com exemplos reais de código. Se você acompanha o desenvolvimento da linguagem, está no caminho certo.
\n\n1. Genéricos tornam-se adultos (Go 1.24)
\n\nOs genéricos no Go surgiram na versão 1.18, mas muitos desenvolvedores reclamavam das limitações: não era possível usar tipos genéricos em métodos, e a sintaxe era excessivamente verbosa. Em 2025, a situação muda radicalmente.
\n\nNovas funcionalidades: tipos genéricos em métodos
\n\nAntes, para escrever uma função que funcionasse com qualquer slice, era necessário criar uma função separada, e não um método. Agora, essa limitação foi removida:
\n\npackage main\n\nimport "fmt"\n\n// Nova sintaxe: método com genérico\ntype Stack[T any] struct {\n items []T\n}\n\nfunc (s *Stack[T]) Push(item T) {\n s.items = append(s.items, item)\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n if len(s.items) == 0 {\n var zero T\n return zero, false\n }\n item := s.items[len(s.items)-1]\n s.items = s.items[:len(s.items)-1]\n return item, true\n}\n\nfunc main() {\n stack := Stack[int]{}\n stack.Push(10)\n stack.Push(20)\n val, ok := stack.Pop()\n fmt.Println(val, ok) // 20 true\n}\n\nIsso simplifica a criação de estruturas de dados genéricas e torna o código mais limpo. Não é mais necessário escrever gambiarras com interfaces.
\n\n2. Revolução no tratamento de erros: try e contextos integrados
\n\nO tratamento de erros no Go sempre foi motivo de debate. A construção if err != nil é poderosa, mas cansativa. Em 2025, a comunidade finalmente recebeu uma alternativa.
Sintaxe proposta para try
\n\nEmbora a sintaxe exata ainda esteja em discussão, espera-se que no Go 1.25 surja a palavra-chave try, que reduzirá o código repetitivo:
package main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc readConfig() (string, error) {\n data, err := os.ReadFile("config.yaml")\n if err != nil {\n return "", err\n }\n return string(data), nil\n}\n\n// Nova versão com try (sintaxe proposta)\nfunc readConfigV2() (string, error) {\n data := try os.ReadFile("config.yaml")\n return string(data), nil\n}\n\nfunc main() {\n cfg, err := readConfigV2()\n if err != nil {\n fmt.Println("Erro:", err)\n return\n }\n fmt.Println("Config:", cfg)\n}\n\nIsso não substitui a abordagem antiga, mas oferece uma escolha. Para casos simples — try, para lógica complexa — o clássico if err.
3. Iteradores e for-range de nova geração
\n\nO Go sempre foi conservador em relação a loops. Mas com o crescimento da popularidade da programação funcional, a linguagem introduz iteradores nativos. Isso permite escrever código mais expressivo sem perda de desempenho.
\n\nExemplo: iterador para árvore
\n\npackage main\n\nimport "fmt"\n\ntype TreeNode struct {\n Value int\n Left *TreeNode\n Right *TreeNode\n}\n\n// Iterador para travessia em profundidade (in-order)\nfunc (n *TreeNode) Iter() func() (int, bool) {\n stack := []*TreeNode{}\n current := n\n\n return func() (int, bool) {\n for current != nil || len(stack) > 0 {\n if current != nil {\n stack = append(stack, current)\n current = current.Left\n } else {\n node := stack[len(stack)-1]\n stack = stack[:len(stack)-1]\n val :=