unsafe.Pointer in Go: The 4 Patterns the Rules Actually Allow
Book: The Complete Guide to Go Programming Also by me: Hexagonal Architecture in Go — the companion book in the Thinking in Go series My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools Me: xgabriel.com | GitHub You reach for unsafe.Pointer to skip a copy. Maybe a []byte you want as a string without the allocation, maybe a struct you want to reinterpret as another. The code compiles. go vet stays quiet. Tests pass. Then, weeks later, under GC pressure, a rare crash shows up in a place that has nothing to do with your change. The Go unsafe package documentation is one long doc comment. It lists the conversions that are valid and warns that everything else is not portable and not guaranteed to keep working. The trouble is that "everything else" includes a lot of code that looks obviously correct. The rules are narrow on purpose. There are effectively four patterns you are allowed to write, and the standard library stays inside all four. Here they are, in the shape you will actually use them in Go 1.23+. The one fact under every rule unsafe.Pointer is a pointer the garbage collector understands. It keeps the object it points at alive and it moves with the object if the runtime relocates a stack. uintptr is a plain integer. The GC does not treat it as a reference. Convert a pointer to a uintptr , store that integer in a variable, and as far as the runtime is concerned nothing points at that memory anymore. That single fact is behind three of the four patterns. Every legal conversion either avoids uintptr entirely or keeps it inside one expression where the compiler can prove the pointer stays live. Pattern 1: reinterpret *T1 as *T2 with the same layout The first pattern converts a *T1 to *T2 when the two types have the same memory shape. You take the address, run it through unsafe.Pointer , and cast to the target pointer type. The standard library does exactly this in math.Float64bits : func Float64bits ( f float64