I’d like to make an alternative proposal for unsafe unions. Rather than using unsafe enum and tagged field references, I’d instead suggest using a struct-like construct and dotted field accessors. For instance:
#[repr(C,union)]
struct U {
f1 : T1,
f2 : T2,
}
fn f(u : U) {
let x = unsafe { u.f1 };
do_something_with(x);
}
fn g(x : T1) -> U {
U { f1 : x }
}
The syntax #[repr(C,union)] struct should be read as union; however, this syntax avoids adding a new keyword for unions.
The dotted field syntax naturally chains: within an unsafe block, you can write s.u.f1.g3, where u has a union type. This seems like the most natural syntax for a union, and it provides maximum familiarity for users of C APIs via FFI.
Safety properties: Any reference to a union field, either as an access, assignment, or initializer, requires an “unsafe” block. Safe code can opaquely pass around a union or a type containing a union. No hard requirements on the field types, but the compiler should produce a warning on putting a type with “Drop” into a union.
Rust code (with appropriate unsafe blocks) may assign into one field and access from another. This has the same semantics as std::mem::transmute; the unsafe code has the responsibility to do so only on appropriate types.
The union has the size of its largest field type, and the maximum alignment of any of its field types.
Union fields may use pub.
Using #[repr(C,union)] on a tuple struct, or on anything other than a struct, produces an error.
Using #[repr(union)] without the C does not have a defined layout (though in practice it’ll likely not change anything). If we want to reserve that for some other behavior, we could have it produce an error instead. Or we could make union imply C.
Optionally, we could also introduce anonymous unions within structures in the same RFC, or introduce those in a later RFC.