Tagged Unions in C: Practical Applications

Tagged unions, also known as discriminated unions or variant records, are critical design patterns in C that pair a standard union with an explicit type indicator (usually an enum) inside a container struct. Because native C unions allocate enough memory for their largest member without tracking which member is currently active, tagged unions provide essential type safety and dynamic data modeling. This article examines the primary real-world systems and practical applications that depend on tagged unions in C, including compiler design, event-handling loops, data serialization, network protocols, and dynamic language runtimes.

Abstract Syntax Trees in Compilers and Parsers

Compilers, transpilers, and interpreters rely heavily on Abstract Syntax Trees (ASTs) to represent source code hierarchical structures. An individual AST node can represent diverse grammatical constructs such as literals, variable declarations, binary expressions, or control flow blocks. Because each node type requires different metadata—a literal holds a primitive value, while a binary expression holds an operator and two child pointers—representing nodes via a uniform struct would waste massive amounts of memory. Tagged unions solve this by defining a common NodeType tag alongside a union containing the specific payload for that syntactic construct, enabling recursive traversals with type-safe switch statements.

Event-Driven Architectures and GUI Frameworks

Graphical user interfaces, game engines, and input libraries (such as SDL or the X Window System) handle continuous streams of asynchronous events. An event can range from a mouse movement containing coordinate offsets to a keystroke event containing virtual key codes, or a window state change with new width and height parameters. Frameworks package these disparate occurrences into a single unified event type. The tagged union allows the core event dispatch queue to receive, store, and distribute generic event structs that callers inspect by reading an event ID field before accessing the corresponding payload.

Heterogeneous Document Parsing (JSON, YAML, BSON)

Data interchange formats like JSON are inherently dynamic, where any key in an object may map to a string, a floating-point number, a boolean, an array, a nested dictionary, or null. When parsing such documents in C without allocating completely distinct object wrappers for every primitive, libraries utilize a generic "Value" tagged union. The tag specifies the parsed JSON data type, allowing the runtime to decode and store complex, arbitrary schemas into contiguous memory blocks while giving consumers predictable ways to extract native C values.

Network Protocol Framing and IPC

Communication protocols and Inter-Process Communication (IPC) systems frequently use variable-format messaging over a single transport channel. A standard packet header often includes a message type field, which dictates how the trailing packet buffer should be interpreted. By mapping the packet body to a tagged union, network software can cast incoming data directly into distinct message variants, such as handshake packets, keep-alive heartbeats, data payloads, or error notifications, without allocating separate buffers for each message variety.

Dynamic Type Systems in Embedded Virtual Machines

Runtimes for dynamically typed languages (such as Lua, MicroPython, or custom domain-specific languages implemented in C) represent runtime values using a tagged union concept often termed a "boxed" or "tagged" value. Each script variable must be capable of changing types at runtime. The host C engine encapsulates variables within a struct that maintains a runtime type descriptor alongside a union holding integers, doubles, raw pointers, or references to garbage-collected heap objects. This guarantees compact memory usage while retaining the flexibility of dynamic typing.