The Ingenious World of Empty Structs in Go: Zeroing in on Zero Memory

cosmicray001
4 min readDec 30, 2023

--

In the dynamic realm of Go programming, there exists a seemingly paradoxical entity — the empty struct

Go’s Zero-Memory Elegance.

This unassuming data type, defined with no fields, holds a unique and powerful trait: it takes up zero memory. In this article, we embark on a journey to unravel the mysteries of the empty struct, exploring the mechanisms that render it memory-free and delving into the scenarios where it shines as a powerful tool.

The Conundrum of Zero Memory:

At first glance, the concept of a struct occupying zero memory might appear counterintuitive. After all, in a world where data storage is paramount, how can a data type be both empty and memoryless? The key lies in understanding the intricacies of Go’s memory allocation and the brilliant design philosophy behind the empty struct.

Width and Alignment:

To appreciate the zero-memory magic, let’s delve into the notion of “width.” In Go, the width of a type refers to the number of bytes it occupies in memory. This is a crucial aspect of memory allocation, ensuring that data is stored efficiently. Width is always a multiple of 8 bits, aligning with the architecture of a computer’s memory.

Consider the width of various types using the unsafe.Sizeof() function. For instance:

var s string
var c complex128
fmt.Println(unsafe.Sizeof(s)) // prints 8
fmt.Println(unsafe.Sizeof(c)) // prints 16

Structs introduce an additional layer of complexity, as the width includes the sum of constituent types plus padding for alignment:

type S struct {
a uint16
b uint32
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 8 (with padding)

The Empty Struct: A Memory Mirage:

Now, let’s unveil the magic of the empty struct. As we’ve established, the width of a type dictates its memory footprint. Surprisingly, the empty struct, being void of fields, has a width of zero:

var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0

This zero-width characteristic is the secret sauce that makes the empty struct memory-free. With no fields to account for and a width of zero, there’s no need for any storage allocation. This is a testament to Go’s elegance in minimizing memory overhead.

Practical Applications:

Using an empty struct in Go is a clever way to implement a set, where the presence or absence of a key is sufficient information. Here’s an example of implementing a set using a map with an empty struct:

package main

import "fmt"

// Set is a basic set data structure implemented using a map with an empty struct.
type Set map[string]struct{}

// Add adds an element to the set.
func (s Set) Add(element string) {
s[element] = struct{}{}
}

// Remove removes an element from the set.
func (s Set) Remove(element string) {
delete(s, element)
}

// Contains checks if the set contains a specific element.
func (s Set) Contains(element string) bool {
_, exists := s[element]
return exists
}

// Size returns the size of the set.
func (s Set) Size() int {
return len(s)
}

// Elements returns a slice of all elements in the set.
func (s Set) Elements() []string {
elements := make([]string, 0, len(s))
for element := range s {
elements = append(elements, element)
}
return elements
}

func main() {
// Create a new set
mySet := make(Set)

// Add elements to the set
mySet.Add("apple")
mySet.Add("banana")
mySet.Add("orange")

// Display the elements in the set
fmt.Println("Set elements:", mySet.Elements())

// Check if an element is present
fmt.Println("Contains 'banana':", mySet.Contains("banana"))
fmt.Println("Contains 'grape':", mySet.Contains("grape"))

// Remove an element
mySet.Remove("apple")

// Display the updated set
fmt.Println("Set elements after removal:", mySet.Elements())

// Display the size of the set
fmt.Println("Size of the set:", mySet.Size())
}
Set elements: [apple banana orange]
Contains 'banana': true
Contains 'grape': false
Set elements after removal: [orange banana]
Size of the set: 2

In this example, the Set type is a map where the keys are strings, and the values are empty structs. The absence or presence of a key in the map indicates whether the element is part of the set. This implementation leverages the zero-width property of an empty struct to minimize memory usage, making it efficient for scenarios where a lightweight set is needed.

Conclusion:

The enigma of the empty struct challenges our conventional notions of data storage. As we traverse the landscape of Go programming, its zero-memory magic serves as a testament to the language’s commitment to efficiency and elegance. Embracing the empty struct opens up new avenues for crafting memory-efficient solutions, showcasing Go’s prowess in balancing simplicity and power in the world of programming.

--

--