tezvyn:

Go's `unsafe` Package: Breaking the Rules for Performance

AI-drafted, machine-checkedSource: pkg.go.devintermediate

Go's `unsafe` package lets you bypass type safety, treating memory like C with raw pointers for performance gains. It's used for low-level optimizations and C interoperability. The footgun: its behavior isn't guaranteed across Go versions, making code fragile.

WHY IT EXISTS Go is a memory-safe language by design, which ensures program stability but can prevent certain low-level optimizations. The unsafe package provides a deliberate escape hatch for the rare cases where a programmer needs to operate below the type system for maximum performance or interoperability with non-Go code.

THE MENTAL MODEL The unsafe package is a deal with the compiler: you tell it to suspend its type-safety rules, and in return, you get the power to manipulate memory directly, like in C. It gives you two main capabilities: a special unsafe.Pointer that can be cast to any pointer type, and unsafe.Add for pointer arithmetic. This lets you inspect and modify memory layout, but you lose all of Go's safety guarantees. You are telling the compiler, "Trust me, I know what I'm doing."

HOW IT WORKS The core is unsafe.Pointer, a generic pointer that can be converted to and from other pointer types. A common pattern is converting a *TypeA to unsafe.Pointer, then to *TypeB to reinterpret the underlying bits. Functions like Sizeof(x), Alignof(x), and Offsetof(x) provide critical information about memory layout. Sizeof returns the size of a type in bytes, while Offsetof returns a struct field's byte offset from the start of the struct. These are essential for performing correct pointer arithmetic.

WHEN TO USE IT Use unsafe sparingly and only when profiling shows a clear bottleneck. Valid use cases include: first, high-performance serialization/deserialization by mapping a byte slice directly to a struct to avoid allocations; second, interoperating with C libraries via cgo, which requires manual memory management; and third, implementing sophisticated, low-level data structures where you need absolute control over memory layout, as seen in parts of the standard library itself.

WHEN NOT TO USE IT Do not use unsafe for clever shortcuts or premature optimization. It makes code harder to read, reason about, and maintain. The biggest danger is that its behavior is not covered by the Go 1 compatibility promise; code that works today might break with a future compiler version. Furthermore, the garbage collector does not track unsafe.Pointer references correctly, which can lead to use-after-free bugs and memory corruption.

ONE CANONICAL EXAMPLE A common, dangerous optimization is converting a byte slice to a string without a memory copy. The safe string(mySlice) allocates a new backing array. The unsafe version creates a string header that points directly to the slice's memory. This is fast but breaks Go's immutability guarantee for strings; if the original slice is modified, the string's content will change unexpectedly.

Read the original → pkg.go.dev

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.