Free — no signup required

Unsafe Pointers: Here Be Dragons

2 min read

Breaking Type Safety

In Go, you cannot cast an *int pointer to a *float64 pointer. The compiler forbids it because the bit representations are different — an int value of 1 and a float64 value of 1.0 have completely different byte layouts in memory. Allowing arbitrary casts would silently corrupt data.

The unsafe package lets you bypass this protection entirely. unsafe.Pointer is a special pointer type that can be cast to any other pointer type, and to uintptr (an integer large enough to hold a memory address).

The three core operations:

import "unsafe"

// 1. Convert any pointer to unsafe.Pointer
p := unsafe.Pointer(&myVar)

// 2. Convert unsafe.Pointer to a different pointer type
q := (*int64)(p)

// 3. Convert to uintptr for pointer arithmetic, then back
addr := uintptr(p) + 8   // move 8 bytes forward in memory
r := (*int64)(unsafe.Pointer(addr))

Why would you ever do this?

  • Zero-copy string/byte-slice conversion: Converting a string to []byte normally allocates a new backing array and copies the data. With unsafe, you can reinterpret the same memory as both types, avoiding the allocation entirely. This matters in high-throughput parsers processing millions of messages per second.
  • System calls: The syscall package uses unsafe.Pointer to pass Go-managed memory buffers directly to the OS kernel, which expects raw addresses.
  • Struct field offsets: unsafe.Offsetof(T.Field) returns the byte offset of a field within a struct, which is used by low-level serialization code to read/write fields without reflection.

The danger is real: If you calculate a pointer address incorrectly and dereference it, the Go runtime cannot save you. There is no bounds check, no garbage collector protection, no recover(). You get a segmentation fault and your program dies instantly. Worse, you might read adjacent memory and get silently wrong data — a bug that is nearly impossible to reproduce.

Interview Tip

A common interview question is: "How would you convert a []byte to a string without allocating?" The unsafe answer is to use unsafe.Pointer to reinterpret the slice header as a string header (both are {ptr, len} internally). The correct follow-up is to immediately note the constraint: the byte slice must not be modified after the conversion, because strings in Go are immutable by contract — mutating the underlying bytes would violate that contract and cause undefined behavior. Demonstrating you know both the technique and its invariants is what separates a strong answer from a dangerous one.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free