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.
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 existsSignal 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
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
