Random Numbers
math/rand/v2 for general-purpose randomness, global functions vs local Source, and crypto/rand for cryptographically secure randomness.
Go's math/rand/v2 package (Go 1.22+) provides pseudo-random number generation for simulations and non-security use cases. For anything security-sensitive - tokens, session IDs, nonces - use crypto/rand.
math/rand/v2 is auto-seeded in Go 1.22+. Call global functions directly - no setup required. rand.IntN(n) returns a non-negative random integer in [0, n). rand.Float64() returns a float in [0.0, 1.0).
package main
import (
"fmt"
"math/rand/v2"
)
func main() {
// Random int in [0, 100)
fmt.Println(rand.IntN(100))
// Random float in [0.0, 1.0)
fmt.Println(rand.Float64())
// Simulate a d6 roll
roll := rand.IntN(6) + 1
fmt.Println("d6:", roll)
// Shuffle a slice
s := []int{1, 2, 3, 4, 5}
rand.Shuffle(len(s), func(i, j int) {
s[i], s[j] = s[j], s[i]
})
fmt.Println(s)
}Create a local rand.Rand with an explicit source when you need reproducible output (e.g., in tests) or an isolated random stream that does not share state with global functions.
package main
import (
"fmt"
"math/rand/v2"
)
func main() {
// Seeded source - deterministic output
src := rand.NewPCG(42, 0)
r := rand.New(src)
fmt.Println(r.IntN(100))
fmt.Println(r.IntN(100))
// Same seed → same sequence
src2 := rand.NewPCG(42, 0)
r2 := rand.New(src2)
fmt.Println(r2.IntN(100)) // same as first value above
}Use crypto/rand for cryptographically secure randomness. rand.Read fills a byte slice with random bytes from the OS. Encode with base64.RawURLEncoding to produce a URL-safe token string.
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
)
func secureToken(n int) string {
b := make([]byte, n)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return base64.RawURLEncoding.EncodeToString(b)
}
func main() {
token := secureToken(32) // 256 bits of entropy
fmt.Println(token) // e.g. "dGhpcyBpcyBhIHRlc3Q..."
// Random UUID v4 (simplified)
var uuid [16]byte
rand.Read(uuid[:])
uuid[6] = (uuid[6] & 0x0f) | 0x40 // version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // variant bits
fmt.Printf("%x-%x-%x-%x-%x\n",
uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
}In production
math/rand/v2 is auto-seeded and safe for simulation, load testing, random sampling, and shuffling. Never use it for security-sensitive randomness - tokens, passwords, nonces, CSRF values, API keys. Use crypto/rand for all of those. The practical pattern: generate N bytes from crypto/rand.Read, base64url-encode them, and you have an opaque token with N*8 bits of entropy. 32 bytes (256 bits) is the conventional floor for tokens. For generating UUIDs in production, use the github.com/google/uuid package rather than rolling your own - it handles version 4 correctly and is extensively tested. In tests that need deterministic sequences (property tests, golden file generators), use rand.NewPCG with a fixed seed so failures are reproducible without relying on a global random state.
Enjoyed this? Get more essays on software craft delivered to your inbox.
Subscribe free