Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add union items to the reference #57

Merged
merged 1 commit into from
May 30, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions src/items.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down