Unions
A union declares overlapping members: every member
starts at offset zero, so writing one member is visible through the
others. Unions are for type punning and for matching C unions imported
from headers.
union IntOrFloat {
int32 i;
float32 f;
}
void main() {
IntOrFloat u = undefined;
u.i = 42;
println(u.i); // prints 42
u.f = 1.5; // overwrites the same 4 bytes
println(u.i); // prints 1069547520, the bits of 1.5f
println(sizeof(IntOrFloat)); // prints 4
}Unions have no constructors: declare a value and assign its members. Unions can have methods and can be generic, but they cannot implement interfaces and members cannot have default values. Unions copy by value unless they declare a destructor, in which case their values move like structs.
Union layout and printing
A union in cx has the same layout as the equivalent C
union, so values can cross extern "C"
boundaries the same way as imported C unions. There is no
print overload for unions: the compiler cannot know which
member is active.
Destruction
Union destruction rules
Members overlap, so at most one member holds a live value at a time.
An owning member is therefore rejected unless it is marked
@manuallyDestroy, in which case the union must declare a
destructor that destroys it explicitly. Assigning to a member never
destroys the old bytes, since they may hold a different member; destroy
the active member with .deinit() before switching to
another one. A .deinit() on a union member does not consume
the union, but the destructor the union declares still runs when the
scope ends, so the member it destroys must be the active one then. The
compiler does not track which member is active: destroying a member
twice, reading a member after .deinit(), or writing through
a pointer taken to a member while another member is active all destroy
or read bytes as the wrong type. Only take a member's address when that
member is active and stays active for the pointer's lifetime.
union Manual {
@manuallyDestroy List<int> items;
int32 tag;
~Manual() {
items.deinit();
}
}
void main() {
Manual m = undefined;
m.tag = 1;
m.items = List([1, 2, 3]); // overwrites tag without destroying it
m.items.deinit(); // destroy before switching back
m.tag = 2;
m.items = List([4]); // 'items' must be active when 'm' is destroyed
}