unsafe
Use unsafe only when you can justify a measurable need and maintain strict invariants around memory assumptions.
Canonical guidance
- prefer safe Go first
- use
unsafeonly for a specific, justified need such as low-level interop or measured performance work - keep unsafe regions tiny and heavily constrained
- document invariants and assumptions explicitly
Use when
- unavoidable low-level interop
- carefully measured hot paths
- runtime-adjacent work
Avoid
- using
unsafeto look clever - relying on undocumented layout assumptions casually
- spreading unsafe usage through ordinary business logic
Preferred pattern
type header struct {
Len uint32
Kind uint16
}
func readHeader(p unsafe.Pointer) header {
// p must point to at least unsafe.Sizeof(header{}) bytes of valid memory.
return *(*header)(p)
}
Anti-pattern
- broad pointer reinterpretation with unclear lifetime and aliasing rules
Explanation: This anti-pattern is tempting in hot paths, but once assumptions about layout and lifetime are implicit the code becomes fragile across change.
Why
- unsafe code bypasses the language’s main safety guarantees
- the maintenance burden must be justified by concrete benefit
Sources
- unsafe package - Go Team
- The Go Programming Language Specification - Go Team