From 81db07970d0b804c522dc4c026e2ff46370536bc Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 18 May 2017 15:36:47 +0300 Subject: [PATCH] Add union items to the reference --- src/items.md | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/src/items.md b/src/items.md index 8d4b5ff22..d5eef800a 100644 --- a/src/items.md +++ b/src/items.md @@ -21,6 +21,7 @@ There are several kinds of item: * [type definitions](#type-aliases) * [struct definitions](#structs) * [enumeration definitions](#enumerations) +* [union definitions](#unions) * [constant items](#constant-items) * [static items](#static-items) * [trait definitions](#traits) @@ -568,6 +569,150 @@ let x = Foo::Bar as u32; // x is now 123u32 This only works as long as none of the variants have data attached. If it were `Bar(i32)`, this is disallowed. +## Unions + +A union declaration uses the same syntax as a struct declaration, except with +`union` in place of `struct`. + +```rust +#[repr(C)] +union MyUnion { + f1: u32, + f2: f32, +} +``` + +The key property of unions is that all fields of a union share common storage. +As a result writes to one field of a union can overwrite its other fields, +and size of a union is determined by the size of its largest field. + +A value of a union type can be created using the same syntax that is used for +struct types, except that it must specify exactly one field: + +```rust +# union MyUnion { f1: u32, f2: f32 } + +let u = MyUnion { f1: 1 }; +``` + +The expression above creates a value of type `MyUnion` with active field `f1`. +Active field of a union can be accessed using the same syntax as struct fields: + +```rust,ignore +let f = u.f1; +``` + +Inactive fields can be accessed as well (using the same syntax) if they are +sufficiently layout compatible with the +current value kept by the union. Reading incompatible fields results in +undefined behavior. +However, the active field is not generally known statically, so all reads of +union fields have to be placed in `unsafe` blocks. + +```rust +# union MyUnion { f1: u32, f2: f32 } +# let u = MyUnion { f1: 1 }; + +unsafe { + let f = u.f1; +} +``` + +Writes to `Copy` union fields do not require reads for running destructors, +so these writes don't have to be placed in `unsafe` blocks + +```rust +# union MyUnion { f1: u32, f2: f32 } +# let mut u = MyUnion { f1: 1 }; + +u.f1 = 2; +``` + +Commonly, code using unions will provide safe wrappers around unsafe +union field accesses. + +Another way to access union fields is to use pattern matching. +Pattern matching on union fields uses the same syntax as struct patterns, +except that the pattern must specify exactly one field. +Since pattern matching accesses potentially inactive fields it has +to be placed in `unsafe` blocks as well. + +```rust +# union MyUnion { f1: u32, f2: f32 } + +fn f(u: MyUnion) { + unsafe { + match u { + MyUnion { f1: 10 } => { println!("ten"); } + MyUnion { f2 } => { println!("{}", f2); } + } + } +} +``` + +Pattern matching may match a union as a field of a larger structure. In +particular, when using a Rust union to implement a C tagged union via FFI, this +allows matching on the tag and the corresponding field simultaneously: + +```rust +#[repr(u32)] +enum Tag { I, F } + +#[repr(C)] +union U { + i: i32, + f: f32, +} + +#[repr(C)] +struct Value { + tag: Tag, + u: U, +} + +fn is_zero(v: Value) -> bool { + unsafe { + match v { + Value { tag: I, u: U { i: 0 } } => true, + Value { tag: F, u: U { f: 0.0 } } => true, + _ => false, + } + } +} +``` + +Since union fields share common storage, gaining write access to one +field of a union can give write access to all its remaining fields. +Borrow checking rules have to be adjusted to account for this fact. +As a result, if one field of a union is borrowed, all its remaining fields +are borrowed as well for the same lifetime. + +```rust,ignore +// ERROR: cannot borrow `u` (via `u.f2`) as mutable more than once at a time +fn test() { + let mut u = MyUnion { f1: 1 }; + unsafe { + let b1 = &mut u.f1; + ---- first mutable borrow occurs here (via `u.f1`) + let b2 = &mut u.f2; + ^^^^ second mutable borrow occurs here (via `u.f2`) + *b1 = 5; + } + - first borrow ends here + assert_eq!(unsafe { u.f1 }, 5); +} +``` + +As you could see, in many aspects (except for layouts, safety and ownership) +unions behave exactly like structs, largely as a consequence of inheriting their +syntactic shape from structs. +This is also true for many unmentioned aspects of Rust language (such as +privacy, name resolution, type inference, generics, trait implementations, +inherent implementations, coherence, pattern checking, etc etc etc). + +More detailed specification for unions, including unstable bits, can be found in +[RFC 1897 "Unions v1.2"](https://github.com/rust-lang/rfcs/pull/1897). + ## Constant items A *constant item* is a named _constant value_ which is not associated with a