A union in Rust is a user-defined composite type where all declared fields share the same underlying memory allocation. The size of a union is determined by its largest field, plus any necessary padding required to satisfy alignment constraints. Because the compiler cannot track which field (or “variant”) was last written to, reading from a union bypasses Rust’s standard type safety guarantees and is an inherentlyDocumentation Index
Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt
Use this file to discover all available pages before exploring further.
unsafe operation.
Declaration
Unions are declared using theunion keyword, followed by a set of named and typed fields. The syntax is identical to that of a standard C-style struct.
Instantiation
To instantiate a union, you must specify exactly one field to initialize. This establishes the initial bit pattern in the shared memory space.Memory Layout
Unlike anenum (which includes a hidden discriminant tag to track the active variant at runtime), a union contains no metadata.
If a union contains a u8 (1 byte) and a u64 (8 bytes), the total size of the union will be 8 bytes, aligned to the 8-byte boundary. Writing to the u8 field will only overwrite the first byte of the 8-byte allocation, leaving the remaining 7 bytes in an indeterminate state based on whatever was previously stored there.
Field Access and Safety
Writing to a union field is generally safe, as it merely overwrites the bits in the shared memory location. However, reading from a union field requires anunsafe block. The compiler relies entirely on the developer to ensure that the memory is being interpreted as the correct type.
f32 field when a u32 was written) does not cause a compilation error. Instead, it results in a bit-cast, which may produce garbage data or trigger undefined behavior if the bit pattern is invalid for the target type (such as an invalid bool or char).
Pattern Matching
You can use pattern matching to extract values from a union. A union pattern can only specify exactly one field per pattern. Because matching on a union field is equivalent to reading it, the destructuring must occur within anunsafe block.
Rust strictly enforces that union patterns must be irrefutable. You cannot use a refutable pattern to match a specific value directly in the union pattern (e.g., IntOrFloat { i: 42 } will be rejected by the compiler). Instead, you must bind the field to a variable.
While you can create a multi-arm match statement using match guards, this introduces significant risk. Because union patterns are irrefutable, multi-arm matches are only possible if the preceding arms use guards (e.g., IntOrFloat { i } if i == 0 => ...). Match guards evaluate sequentially, meaning a guard will read the field’s memory to evaluate the condition. If the underlying bit pattern is invalid for the type being evaluated in the guard, this sequential read will trigger undefined behavior before the correct arm is ever reached.
Drop Behavior and Non-Copy Types
By default, unions work seamlessly with types that implement theCopy trait. Because the compiler does not know which field is currently active, it cannot safely determine which destructor to run when the union goes out of scope.
To prevent undefined behavior during destruction, Rust requires that any non-Copy type (regardless of whether it explicitly implements Drop or not) must be wrapped in std::mem::ManuallyDrop<T> to be placed in a union. This explicitly disables automatic cleanup, forcing the developer to manually drop the inner value using unsafe code before the union is destroyed.
Master Rust with Deep Grasping Methodology!Learn More





