Course contentsShow
← Rust
Lesson 1708 of 1,91015. Performance and Architectural EngineeringFree lesson

Representing Enums Efficiently

Analyze tag storage and discriminants to minimize the size of tagged unions.

In this lesson, you will learn how to minimize the memory footprint of Rust enums by understanding how the compiler manages tag storage and discriminants.

The Anatomy of an Enum

In Rust, enums are "tagged unions." To distinguish which variant is currently active, the compiler adds a hidden field known as the tag (or discriminant). While a simple C-style enum (like enum Color { Red, Green }) might occupy only 1 byte, complex enums containing data are subject to both the tag's size and the alignment requirements of the largest variant's fields.

Analyzing the Discriminant

Consider this enum:

enum Message {
    Quit,              // 0 bytes of data
    Move { x: i32, y: i32 }, // 8 bytes of data
}

Because Move requires 8 bytes, the compiler will typically allocate 12 bytes total: 8 for the i32 fields, plus at least 4 bytes for the tag (to satisfy the alignment of the i32 payload).

Optimization through Layout Control

If you have a massive enum, you can optimize its footprint by manually controlling the representation or the discriminant. Using the #[repr(u8)], #[repr(u16)], etc., attributes forces the compiler to use a specific integer size for the tag.

Furthermore, if your enum has many variants but only one carries a large payload, the compiler attempts to optimize the layout using null pointer optimization. If one variant is a pointer-like type (like Box, &T, or fn), the compiler can use the "invalid" bit pattern of that pointer (the null value) as the tag itself. This effectively reduces the enum size to the size of the pointer, eliminating the need for an explicit tag field.

// This enum is the same size as a pointer (8 bytes on 64-bit)
// because the discriminant is folded into the null state of the Box.
enum Node {
    Empty,
    Content(Box<[u8; 1024]>),
}

By structuring your data so that common variants utilize these "niche" patterns or by grouping large data payloads into separate heap-allocated structures, you prevent the entire enum from expanding to accommodate the "worst-case" variant size.

Key Takeaway: To minimize enum size, utilize niche-filling types like Box or Option to fold tags into unused bit patterns, and use repr attributes to control discriminant storage when the default layout incurs unnecessary padding.