Empty structs

March 20, 2023

We finally we have enough knowledge for the EBNF format not to seem completely foreign, so let’s jump back and take a look at that, with the examples provided in the spec…

Struct types

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
Tag           = string_lit .
// An empty struct.
struct {}

// A struct with 6 fields.
struct {
	x, y int
	u float32
	_ float32  // padding
	A *[]int
	F func()

What’s that? An empty struct? Why would you ever…?

Let’s talk about that. It’s only mentioned here as a passing example, but the empty struct can actually be quite useful.

The interesting thing about the empty struct is that it does not take up any space.

var x struct{}
fmt.Println(unsafe.Sizeof(x)) // Prints: 0

This makes it useful in two particular cases that come up fairly frequently.

The first is when you’re tracking existence or uniqueness in a map. Use a struct{} instead of a bool value:

names := []string{"Bob", "Alice", "Bob", "Chuck", "Chuck", "Bob"}
var uniqueNames := map[string]struct{}{}
for _, name := range names {
	uniqueNames[name] = struct{}{}

The common practice of using map[string]bool for this type of operation is problematic in two ways: First, and honestly least important in most cases, it uses memory unnecessarily, by allocating an extra byte per map value. More important, it’s semantically less clear. A type of map[string]bool suggests that the truthiness or falsiness of the boolean value has significance. I’ve wasted far too much time reading and debugging codebases that did this, to try to determine if the boolean value had any significance. Save your readers some effort (and ease the garbage collector’s duties at the same time), and just use struct{} instead. Even if it is a teeny bit more typing.

The second use case is when using a channel to signal an event, without any data:

tick := make(chan struct{})

It is not possible to send data over this channel, but it is possible to send signals, which can sometimes be all that’s needed.

Quotes from The Go Programming Language Specification Version of December 15, 2022

Share this