Unions
A union declares a type whose members all share one piece of storage. A value holds the bytes of one member, and reading any member reads those same bytes as that member's type — nothing is converted and nothing is checked. A union records no tag, so the program, or the foreign interface it talks to, is responsible for knowing which member is meaningful.
union = [ attributes ] [ "pub" ] "union" name "{" [ member { "," member } [ "," ] ] "}"
member = name ":" type
union Bits {
asFloat: float32,
asUnsigned: uint32,
asBytes: uint8[4]
}
Members are separated by commas, unlike struct fields; a ; after a member is expected ',' between union fields before ';'.
Building a union
A union literal names exactly one member and gives its value:
let one = Bits { asFloat: 1.0f32 };
Naming none, or more than one, is an error: union initializer for 'Bits' must select exactly one field, but 2 were provided.
var bits: Bits; declares a union without a value. Writing any one member initializes it, and from then on every member may be read.
Reading and writing members
Members are read and written with ., like struct fields. Every member starts at the same address, so a write through one member is seen through all the others, reinterpreted:
let one = Bits { asFloat: 1.0f32 };
PrintLine("{:x}", one.asUnsigned); // 3f800000, the bit pattern of 1.0
PrintLine("{}", one.asBytes[3]); // 63, its high byte on a little-endian target
var bits = Bits { asUnsigned: 0x40000000u32 };
PrintLine("{}", bits.asFloat); // 2.0
bits.asBytes[3] = 0x3Fu8;
PrintLine("{}", bits.asFloat); // 0.5
Reading a member other than the one last written is not an error; it is the reason a union exists. The result depends on the target's byte order and on the members' representations, so code that relies on it is code about a particular layout.
A member narrower than the union leaves the rest of the storage as it was: writing a uint8 member of a union that also holds a uint64 changes only the low byte.
Layout
A union is as large as its largest member, rounded up to its alignment, and as aligned as its most-aligned member. sizeof(Bits) is 4. See Memory layout.
A union is a value: assigning it copies its bytes, and the copy is independent of the original.
No ownership
A union does not know which member it holds, so it never runs a destructor for any member. A member whose type has a destructor is stored without it ever running; keep such values out of unions, or destroy them by hand.
When to use a union
| Use | Instead |
|---|---|
A C union in a foreign interface | — |
| Examining the bits of a value of another type | — |
| One of several alternatives, known by the compiler | a variant, whose tag is checked by match |
| A value of one of several types | a sum type |
See also
- Variants — alternatives with a tag
- FFI — unions in foreign interfaces
- Memory layout — sizes and alignment
- Learn: Union
Matching
Taking a variant apart with match: case patterns for unit, positional and named cases, binding payloads, nesting, guards and exhaustiveness.
Overview
Tuple types and values: positional elements read with .0, the unit type (), structural equality without ordering, and when to choose a struct instead.