rokdocs
v0.1

2.5. union

union is an untagged container whose fields share the same memory. The language does not track which field's value is meaningful at any moment; that is the user's responsibility.

union_decl      ::= "union" identifier generics? where_clause? "{" named_field_list? "}"

2.5.1. Basic form

The simplest union is declared as:

union EpollData {
    ptr: usize;
    fd: i32;
    val: u64;
}

The body and field syntax are the same as the named struct of struct. The differences are the keyword union and the semantics: all fields share the same memory location.

Generic unions

A union may have type parameters (2.10), with bounds, a where clause and defaults as for a struct. Each instance is its own type with its own layout: its size and alignment follow its field types for those arguments.

union Bits<T> { v: T; raw: u64; }
union Small<T = u16> { v: T; raw: u8; }

let a = Bits::<f64> { raw: 0x3FF0000000000000u64 };
let b = Bits { v: 7i32 };              // Bits<i32>, inferred from the field given
let x = unsafe { a.v };                // 1.0 (the bits of 1.0f64)

In a union literal the arguments are inferred from the one field given. Bounds are checked per instance, and diagnostics name the instance (reading field `v` of union `Bits<i32>` needs an `unsafe` block). A union declares at least one field.

2.5.2. Semantics

The following example shows how a union behaves.

let x = EpollData { fd: 5i32 };
x.fd = 7i32;                    // write - safe
let y = unsafe { x.val };       // read - requires unsafe; reinterprets x's bit pattern as u64

Creating an instance with some field writes that field's bits to memory. Reading it later through another field reinterprets the same memory bits as that field's type. Writing a field is safe, but reading a field is allowed only inside unsafe, because the language does not guarantee that the bits read are a valid value of that type (e.g. bool, char, pointers).

2.5.3. Where safety lies

Bit reinterpretation by a union is behavior defined by the language, but the meaning of the result is the program domain's responsibility. For example, writing 1.0 to an f32 field and reading the i32 field gives 1065353216 (0x3F800000); conversely, reading i32 bits as f32 gives the real value given by the IEEE 754 bit representation. The language does not check whether that value is meaningful for the domain.

This model differs from C's union. In C, reading from a different member is undefined behavior; in rok it is defined as bit reinterpretation. Since the result is not guaranteed to be a valid value, reads are marked unsafe.

2.5.4. Default layout is left to the compiler

The default union layout is left to the compiler. It need only satisfy the size and alignment of the largest field; the exact field alignment and padding are decided by the compiler.

If a C ABI-compatible union is needed, attach #[repr(C)].

#[repr(C)]
union CUnion {
    a: i32;
    b: f32;
}

A union with #[repr(C)] is bit-compatible with C's union and is used in FFI interfaces. The exact meaning is defined in Built-in attributes.

2.5.5. union vs #[repr(C)] union

The two forms differ as follows.

FormUsed inLayoutBit compatibility
union T { ... }Ordinary codeLeft to compilerGuaranteed only within rok
#[repr(C)] union T { ... }FFISame as C ABICompatible with C code

2.5.6. Visibility / duplicate definitions

The same rules as struct apply.

Warning

Reading a union field is a bit reinterpretation and is allowed only inside unsafe (writing is safe). Whether the bits read are a valid value of that type, and whether it is meaningful for the domain, is the author's responsibility.

2.5.7. union vs sum types

union and sum types may look similar, but they are entirely different tools.

union  Foo { a: i32; b: f32; }    // same memory, no tag
type   Bar = i32 | f32;            // separate memory, tagged

The differences in each aspect:

AspectunionSum type (A | B)
Tracks which field is activeNoTracked by tag
Conversion between fieldsBit reinterpretationVariant change (value rebuilt)
Memory useSize of the largest fieldTag + size of the largest variant
Reading a fieldRequires unsafe (bit reinterpretation)Safe, via patterns / is

The two serve completely different purposes: union provides different interpretations at the bit level, and a sum type expresses different kinds at the value level. They must not be confused.

Edit this page on GitHub