Skip to content

Commit

Permalink
Auto merge of #737 - jamesmunns:using-unions, r=fitzgen
Browse files Browse the repository at this point in the history
Add union tutorial

Here is a first addition to describe usage of unions. Please let me know if there are any changes you would like, or additional features to cover.

For now, I have described both ways to interact with unions, as the new union type has not stabilized.
  • Loading branch information
bors-servo authored Jun 10, 2017
2 parents c0389c3 + c094c93 commit 31c06fb
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
- [Replacing One Type with Another](./replacing-types.md)
- [Preventing the Derivation of `Copy` and `Clone`](./nocopy.md)
- [Generating Bindings to C++](./cpp.md)
- [Using Unions](./using-unions.md)
132 changes: 132 additions & 0 deletions book/src/using-unions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Using the Union Types Generated by Bindgen

**NOTE:** As of Rust version 1.17, Rust does not have a stable `union` type. Issue [#32836](https://github.com/rust-lang/rust/issues/32836) tracks the stabilization of a `union` type in Rust. By default, bindgen will generate the preliminary unstable `union` type, unless the flag `--no-unstable-rust` flag is used.

In general, most interactions with unions (either reading or writing) are unsafe.

For this discussion, we will use the following C type definitions:

```c
typedef struct {
int32_t a;
int32_t b;
} alpha_t;

typedef struct {
uint32_t c;
uint16_t d;
uint16_t e;
uint8_t f;
} beta_t;

typedef union {
alpha_t alfa;
beta_t bravo;
} greek_t;
```

## Relevant Bindgen Options

### Library

* [`bindgen::Builder::no_unstable_rust()`](https://docs.rs/bindgen/0.25.3/bindgen/struct.Builder.html#method.no_unstable_rust)
* [`bindgen::Builder::derive_default()`](https://docs.rs/bindgen/0.25.3/bindgen/struct.Builder.html#method.derive_default)

### Command Line

* `--no-unstable-rust`
* `--with-derive-default`

## Using the unstable `union` version

With `struct`s generated by bindgen from C, it is possible to initialize fields in a "normal" rust way:

```rust,ignore
mod bindings;
fn main() {
let x = bindings::alpha_t {
a: 1,
b: -1,
};
}
```

When using the unstable `union` type, there are two choices for initialization: Zeroed, and with a specific variant.

```rust,ignore
#![feature(untagged_unions)]
mod bindings_unstable;
fn unstable() {
// Initalize the union to zero
let x = bindings_unstable::greek_t::default();
// If `--with-derive-default` option is not used, the following may be used
// to initalize the union to zero:
let x = unsafe{ std::mem::zeroed::<bindings_unstable::greek_t>() };
// Or, it is possible to initialize exactly one variant of the enum:
let x = bindings_unstable::greek_t {
alfa: bindings_unstable::alpha_t {
a: 1,
b: -1,
},
};
unsafe {
println!("{:?}", z.alfa); // alpha_t { a: 1, b: -1 }
println!("{:?}", z.bravo); // beta_t { c: 1, d: 65535, e: 65535, f: 127 }
}
}
```

## Using the stable BindgenUnion types

For versions of Rust that do not support the new `union` type, bindgen will generate types which provide union-like access to structure fields.

Interacting with these unions is slightly different than the new `union` types. Whenever a variant of the union is accessed, it must be done through a reference.

```rust,ignore
mod bindings;
fn stable() {
// `default()` or `zeroed()` may still be used with Bindgen's Union types
let mut x = bindings::greek_t::default();
// This will not work:
// let x = bindings::greek_t {
// alfa: bindings::alpha_t {
// a: 1,
// b: -1,
// },
// };
// Instead, access the field through `.as_ref()` and `.as_mut()` helpers:
unsafe {
*x.alfa.as_mut() = bindings::alpha_t {
a: 1,
b: -1,
};
println!("{:?}", x.alfa.as_ref()); // alpha_t { a: 1, b: -1 }
println!("{:?}", x.bravo.as_ref()); // beta_t { c: 1, d: 65535, e: 65535, f: 0 }
}
```

If you attempt to access a BindgenUnion field directly, you will see errors like this:

```text
error[E0308]: mismatched types
--> src/main.rs:44:15
|
44 | alfa: bindings::alpha_t {
| _______________^
45 | | a: 1,
46 | | b: -1,
47 | | },
| |_________^ expected struct `bindings::__BindgenUnionField`, found struct `bindings::alpha_t`
|
= note: expected type `bindings::__BindgenUnionField<bindings::alpha_t>`
found type `bindings::alpha_t`
```

0 comments on commit 31c06fb

Please sign in to comment.