Go Generics em 2026: Parâmetros de Tipo, Constraints e Perguntas de Entrevista
Guia completo sobre generics em Go: parâmetros de tipo, interfaces de constraint, padrões avançados e perguntas técnicas frequentes em entrevistas de emprego para desenvolvedores Go.

Os generics representam uma das evoluções mais significativas da linguagem Go desde sua criação. Introduzidos com Go 1.18 em 2022, os parâmetros de tipo permitem escrever código reutilizável sem sacrificar a segurança da tipagem estática. Em 2026, os generics se tornaram uma ferramenta indispensável para todo desenvolvedor Go, e seu domínio constitui um critério de avaliação fundamental durante as entrevistas técnicas. Este artigo explora em profundidade os parâmetros de tipo, as constraints de interface, os padrões avançados e as perguntas de entrevista mais frequentes.
Antes do Go 1.18, os desenvolvedores precisavam duplicar o código para cada tipo ou utilizar interface com asserções de tipo custosas em performance e propensas a erros em tempo de execução. Os generics permitem escrever uma única implementação que funciona com qualquer tipo que satisfaça as constraints especificadas, mantendo a verificação de tipos em tempo de compilação.
Sintaxe dos Parâmetros de Tipo
A sintaxe de generics em Go utiliza colchetes para declarar os parâmetros de tipo. Um parâmetro de tipo é definido com um nome seguido de uma constraint que especifica as operações permitidas sobre esse tipo.
package main
import "fmt"
// Generic function with type parameter T
func Print[T any](value T) {
fmt.Println(value)
}
// Generic function with multiple type parameters
func Map[T, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
}
return result
}
func main() {
Print(42)
Print("hello")
Print(3.14)
numbers := []int{1, 2, 3, 4, 5}
doubled := Map(numbers, func(n int) int { return n * 2 })
fmt.Println(doubled) // [2 4 6 8 10]
strings := Map(numbers, func(n int) string {
return fmt.Sprintf("num:%d", n)
})
fmt.Println(strings) // [num:1 num:2 num:3 num:4 num:5]
}A palavra-chave any é um alias para interface{} e representa a constraint mais permissiva. Quando um parâmetro de tipo utiliza any, nenhuma operação específica é garantida sobre esse tipo, exceto a atribuição e a passagem como parâmetro.
Constraints de Interface e Tipos Predefinidos
As constraints permitem restringir os tipos aceitos por uma função ou struct genérica. Go 1.18 introduziu o pacote constraints (movido para cmp e slices no Go 1.21+) assim como a possibilidade de definir constraints personalizadas.
package main
import (
"cmp"
"fmt"
)
// Custom constraint using type union
type Number interface {
int | int32 | int64 | float32 | float64
}
// Function constrained to Number types
func Sum[T Number](numbers []T) T {
var total T
for _, n := range numbers {
total += n
}
return total
}
// Using cmp.Ordered for comparable types
func Max[T cmp.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
// Constraint with method requirement
type Stringer interface {
String() string
}
func PrintAll[T Stringer](items []T) {
for _, item := range items {
fmt.Println(item.String())
}
}
func main() {
ints := []int{1, 2, 3, 4, 5}
fmt.Println(Sum(ints)) // 15
floats := []float64{1.5, 2.5, 3.0}
fmt.Println(Sum(floats)) // 7.0
fmt.Println(Max(10, 20)) // 20
fmt.Println(Max("abc", "xyz")) // xyz
}O operador | permite criar uniões de tipos em uma constraint. O operador ~ (til) permite incluir todos os tipos cujo tipo subjacente corresponda, o que é essencial para tipos personalizados baseados em tipos primitivos.
Structs e Métodos Genéricos
As structs também podem utilizar parâmetros de tipo, permitindo criar containers e estruturas de dados reutilizáveis.
package main
import "fmt"
// Generic Stack implementation
type Stack[T any] struct {
items []T
}
func NewStack[T any]() *Stack[T] {
return &Stack[T]{items: make([]T, 0)}
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
func (s *Stack[T]) Peek() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
return s.items[len(s.items)-1], true
}
func (s *Stack[T]) Len() int {
return len(s.items)
}
// Generic Pair type
type Pair[K, V any] struct {
Key K
Value V
}
func main() {
// Stack of integers
intStack := NewStack[int]()
intStack.Push(1)
intStack.Push(2)
intStack.Push(3)
for intStack.Len() > 0 {
val, _ := intStack.Pop()
fmt.Println(val) // 3, 2, 1
}
// Stack of strings
strStack := NewStack[string]()
strStack.Push("hello")
strStack.Push("world")
// Pair usage
pair := Pair[string, int]{Key: "age", Value: 30}
fmt.Printf("%s: %d\n", pair.Key, pair.Value)
}Os métodos genéricos não podem introduzir novos parâmetros de tipo; eles utilizam os parâmetros declarados no nível da struct.
Inferência de Tipo e Instanciação
O compilador Go pode inferir os parâmetros de tipo a partir dos argumentos fornecidos, simplificando a chamada de funções genéricas.
package main
import "fmt"
func First[T any](slice []T) (T, bool) {
if len(slice) == 0 {
var zero T
return zero, false
}
return slice[0], true
}
func Keys[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
func main() {
// Type inference - no need to specify [int]
nums := []int{1, 2, 3}
first, ok := First(nums)
fmt.Println(first, ok) // 1 true
// Explicit type parameters when needed
empty, ok := First[string]([]string{})
fmt.Println(empty, ok) // "" false
// Map with inferred types
ages := map[string]int{"alice": 30, "bob": 25}
names := Keys(ages)
fmt.Println(names) // [alice bob] or [bob alice]
}A inferência de tipo funciona da esquerda para a direita e não pode inferir tipos que não aparecem nos parâmetros da função.
Padrões Avançados: Interfaces Genéricas e Composição
As interfaces podem ser parametrizadas, permitindo definir contratos genéricos reutilizáveis.
package main
import "fmt"
// Generic Repository interface
type Repository[T any, ID comparable] interface {
FindByID(id ID) (T, error)
Save(entity T) error
Delete(id ID) error
FindAll() ([]T, error)
}
// Generic Result type for error handling
type Result[T any] struct {
value T
err error
}
func Ok[T any](value T) Result[T] {
return Result[T]{value: value}
}
func Err[T any](err error) Result[T] {
return Result[T]{err: err}
}
func (r Result[T]) Unwrap() (T, error) {
return r.value, r.err
}
func (r Result[T]) Map(fn func(T) T) Result[T] {
if r.err != nil {
return r
}
return Ok(fn(r.value))
}
// Generic Option type
type Option[T any] struct {
value *T
}
func Some[T any](value T) Option[T] {
return Option[T]{value: &value}
}
func None[T any]() Option[T] {
return Option[T]{value: nil}
}
func (o Option[T]) IsSome() bool {
return o.value != nil
}
func (o Option[T]) Unwrap() T {
if o.value == nil {
panic("called Unwrap on None")
}
return *o.value
}
func (o Option[T]) UnwrapOr(defaultValue T) T {
if o.value == nil {
return defaultValue
}
return *o.value
}
func main() {
result := Ok(42).Map(func(n int) int { return n * 2 })
val, err := result.Unwrap()
fmt.Println(val, err) // 84 <nil>
opt := Some("hello")
fmt.Println(opt.IsSome()) // true
fmt.Println(opt.Unwrap()) // hello
none := None[int]()
fmt.Println(none.UnwrapOr(0)) // 0
}Perguntas de Entrevista Frequentes sobre Generics em Go
As entrevistas técnicas em Go incluem cada vez mais perguntas sobre generics. A seguir estão as perguntas mais comuns com suas respostas esperadas.
Pergunta 1: Qual é a diferença entre any e comparable?
any é um alias para interface{} e aceita qualquer tipo. comparable é uma constraint predefinida que aceita apenas tipos que suportam os operadores == e !=. Slices, maps e funções não são comparáveis e não satisfazem comparable.
Pergunta 2: Por que não se pode usar operadores aritméticos com any?
Os operadores aritméticos (+, -, *, /) não estão definidos para todos os tipos. É necessário usar uma constraint como Number ou cmp.Ordered para garantir que o tipo suporte essas operações.
// This won't compile - any doesn't support +
func AddWrong[T any](a, b T) T {
return a + b // compile error
}
// Correct approach with constraint
type Numeric interface {
~int | ~int64 | ~float64
}
func Add[T Numeric](a, b T) T {
return a + b // works
}Pergunta 3: Como implementar uma função genérica Filter?
func Filter[T any](slice []T, predicate func(T) bool) []T {
result := make([]T, 0)
for _, v := range slice {
if predicate(v) {
result = append(result, v)
}
}
return result
}
// Usage
numbers := []int{1, 2, 3, 4, 5, 6}
evens := Filter(numbers, func(n int) bool { return n%2 == 0 })
// evens = [2, 4, 6]Pergunta 4: Qual é a diferença entre ~int e int em uma constraint?
O operador til (~) inclui todos os tipos cujo tipo subjacente é int, incluindo tipos definidos como type MyInt int. Sem til, apenas o tipo exato int é aceito.
type MyInt int
// Only accepts int, not MyInt
type StrictInt interface {
int
}
// Accepts int and any type with underlying type int (like MyInt)
type FlexibleInt interface {
~int
}Pronto para mandar bem nas entrevistas de Go?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Limitações e Boas Práticas
Os generics em Go têm certas limitações intencionais para preservar a simplicidade da linguagem. Os métodos não podem ter seus próprios parâmetros de tipo. A especialização de tipo (fornecer uma implementação diferente para um tipo específico) não é suportada. Tipos recursivos com parâmetros de tipo têm restrições.
As boas práticas recomendam usar generics quando eles realmente simplificam o código, não simplesmente porque estão disponíveis. Se uma função funciona apenas com dois ou três tipos concretos, funções separadas podem ser mais claras. Os generics são particularmente úteis para estruturas de dados, algoritmos de coleção e padrões como Result/Option.
Conclusão
Os generics transformam a forma como o código Go é escrito e organizado. Eles permitem criar bibliotecas e abstrações reutilizáveis enquanto conservam a segurança de tipos que torna o Go forte. O domínio dos parâmetros de tipo, das constraints e dos padrões associados se tornou essencial para todo desenvolvedor Go profissional. As perguntas de entrevista sobre este tema avaliam não apenas o conhecimento da sintaxe, mas também a capacidade de reconhecer quando os generics agregam valor real ao código.

Escrito por
Anthony Fillion-MailletDesenvolvedor fullstack, fundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 8 de julho de 2026
Compartilhar
Artigos relacionados

Go SIMD e o Pacote ArchSIMD em 2026: Otimização de Performance e Perguntas de Entrevista
Descubra o pacote simd/archsimd do Go 1.26: operações vetoriais nativas, otimização de performance e preparação para entrevistas técnicas de Go.

Tratamento de Erros em Go: Padrões, Wrapping e Boas Práticas para 2026
Guia completo sobre tratamento de erros em Go: desde a interface error até padrões avançados como wrapping, sentinel errors, errors.Is e errors.As. Domine os conceitos essenciais para entrevistas técnicas e projetos profissionais.

Go e gRPC em 2026: Microsserviços de Alta Performance e Perguntas de Entrevista
Guia completo sobre gRPC com Go em 2026. Protocol Buffers, RPCs unários e streaming, interceptors, padrões de produção, mTLS e perguntas frequentes de entrevista para engenheiros backend.