Aleatoriedade é algo sério demais para deixá-la ao acaso
\n\nTodo mundo que já escreveu código em Go já se deparou com a geração de números aleatórios. O Gerador de Números Aleatórios (RNG) é uma coisa traiçoeira. À primeira vista, parece simples: chame rand.Intn() e obtenha um número. Mas se você não prestar atenção à semeadura correta (seed), sua "aleatoriedade" se transformará em uma sequência previsível que quebrará testes, falhará na criptografia ou simplesmente surpreenderá com resultados idênticos a cada execução.
No Stack Overflow, a pergunta "How to properly seed random number generator" acumulou milhares de visualizações. As pessoas se confundem com time.Now().UnixNano(), usam crypto/rand onde não é necessário e sofrem com baixo desempenho. Hoje vamos analisar este tópico usando Go como exemplo — sem enrolação, com benchmarks reais e soluções prontas.
Por que a seed não é apenas uma formalidade?
\n\nSeed (semente) é o estado inicial do gerador. A mesma semente produz a mesma sequência de números. Isso é útil para depuração, mas desastroso para produção se você espera imprevisibilidade.
\n\nEm Go, o pacote padrão math/rand usa um algoritmo determinístico. Se você esquecer de chamar rand.Seed(), então... Até o Go 1.20, o gerador padrão usava uma semente fixa (geralmente 1). A partir do Go 1.20, math/rand é automaticamente semeado com um valor aleatório na inicialização do programa. No entanto, confiar nisso em locais críticos é uma má prática.
O principal problema dos iniciantes é tentar semear o gerador para cada número aleatório:
\n\n// ❌ RUIM: recriando o gerador toda vez\nfunc getRandom() int {\n r := rand.New(rand.NewSource(time.Now().UnixNano()))\n return r.Intn(100)\n}\n\nPor que isso é ruim? time.Now().UnixNano() fornece nanossegundos, mas na prática a precisão depende do SO. No Windows, por exemplo, pode ser de ~15 ms. Se você chamar a função duas vezes seguidas, obterá números idênticos. Além disso, cada chamada de rand.NewSource cria um novo objeto, sobrecarregando o coletor de lixo.
Top 3 abordagens para semeadura em Go
\n\n1. Método clássico: seed global uma vez
\n\nA opção mais simples e correta para a maioria das aplicações é definir a seed uma vez na inicialização do programa.
\n\npackage main\n\nimport (\n "fmt"\n "math/rand"\n "time"\n)\n\nfunc main() {\n // ✅ BOM: semeamos uma vez\n rand.Seed(time.Now().UnixNano())\n\n for i := 0; i < 5; i++ {\n fmt.Println(rand.Intn(100))\n }\n}\n\nImportante: time.Now().UnixNano() fornece um valor único, mas se o programa for executado várias vezes no mesmo nanossegundo (improvável, mas teoricamente possível), a seed será a mesma. Para 99% das aplicações, isso é suficiente.
2. Gerador local para isolamento
\n\nQuando você tem várias goroutines ou não quer afetar o estado global, crie sua própria instância de rand.Rand.
func NewSeededGenerator() *rand.Rand {\n seed := time.Now().UnixNano()\n return rand.New(rand.NewSource(seed))\n}\n\n// Uso:\nrng := NewSeededGenerator()\nfor i := 0; i < 3; i++ {\n fmt.Println(rng.Intn(100))\n}\n\nEssa abordagem oferece controle total: diferentes goroutines podem ter seus próprios geradores e não bloqueiam umas às outras (o rand global usa um mutex).
3. Seed criptograficamente segura para tarefas críticas
\n\nSe você precisa de aleatoriedade real (tokens, senhas, criptografia) — esqueça o math/rand. Use crypto/rand:
import (\n "crypto/rand"\n "encoding/binary"\n "math/rand"\n)\n\nfunc CryptoSeed() int64 {\n var buf [8]byte\n _, err := rand.Read(buf[:])\n if err != nil {\n