Fundamentals 7 min read

Why Go’s Empty Struct Takes Zero Memory and How to Leverage It

Go’s empty struct occupies zero bytes thanks to a special zerobase address, and understanding this behavior reveals how to use empty structs for memory‑efficient patterns such as sets, signal channels, and struct alignment tricks.

Radish, Keep Going!
Radish, Keep Going!
Radish, Keep Going!
Why Go’s Empty Struct Takes Zero Memory and How to Leverage It

In Go, a normal struct occupies memory, but an empty struct ( struct{}) has a size of 0. This is due to a special global variable zerobase of type uintptr that the compiler uses as the base address for all zero‑size allocations.

Zero‑size Allocation Mechanism

When the compiler encounters a variable of type struct{}, it assigns the address of &zerobase to it. Consequently, every empty‑struct variable shares the same address, and any memory allocation of size 0 returns &zerobase from the runtime function mallocgc.

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
    // ...
    if size == 0 {
        return unsafe.Pointer(&zerobase)
    }
    // ...
}

Demonstration

type Test struct {
    A int
    B string
}
func main() {
    fmt.Println(unsafe.Sizeof(new(Test)))   // size of a normal struct
    fmt.Println(unsafe.Sizeof(struct{}{})) // size of an empty struct (0)
}

Running the program shows 8 for the normal struct and 0 for the empty struct.

Address Sharing Example

package main
import "fmt"

type emptyStruct struct {}

func main() {
    a := struct{}{}
    b := struct{}{}
    c := emptyStruct{}
    fmt.Printf("%p
", &a)
    fmt.Printf("%p
", &b)
    fmt.Printf("%p
", &c)
}
// All three prints produce the same address (e.g., 0x58e360)

Memory Alignment with Empty Structs

If an empty struct appears as the last field of another struct, it can affect alignment. The following code compares two structs where the empty struct is placed differently:

type A struct {
    x int
    y string
    z struct{}
}
type B struct {
    x int
    z struct{}
    y string
}
func main() {
    println(unsafe.Alignof(A{})) // 8
    println(unsafe.Alignof(B{})) // 8
    println(unsafe.Sizeof(A{}))  // 32
    println(unsafe.Sizeof(B{}))  // 24
}

When the empty struct is at the end, extra padding may be added to satisfy alignment, leading to a larger overall size.

Practical Uses of Empty Structs

Because an empty struct consumes no memory, it is ideal for scenarios where only the presence of a key matters.

Set Implementation with Maps

m := make(map[int]struct{})
m[1] = struct{}{}
_, ok := m[1] // ok is true if key exists

Signal Channels

waitc := make(chan struct{})
// send a signal
waitc <- struct{}{}
close(waitc)
// receive the signal
select {
case <-waitc:
    // handle signal
}

Since the struct carries no data, the channel is used purely for synchronization.

Summary

Empty structs are valid structs with a size of 0.

All empty‑struct values share the same address, the address of the internal zerobase variable.

Leveraging the zero‑size property enables memory‑efficient patterns such as set‑like maps and signal‑only channels, and influences struct alignment when placed as the last field.

References

The empty struct, Dave Cheney – https://dave.cheney.net/2014/03/25/the-empty-struct

Go 最细节篇— struct{} 空结构体究竟是啥? – https://www.qiyacloud.cn/2020/12/2020-12-21/

Go Playground examples – https://go.dev/play/

Go runtime source – mallocgc implementation

memory optimizationmapchannelempty structzerobase
Radish, Keep Going!
Written by

Radish, Keep Going!

Personal sharing

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.