blob: 5ae764a33dad7eb3690e51c096f8406975aff47d [file] [view] [edit]
# Using the Union Types Generated by Bindgen
**NOTE**: Rust 1.19 stabilized the `union` type (see Rust issue [#32836](https://github.com/rust-lang/rust/issues/32836)).
You can pass the `--rust-target` option to tell `bindgen` to target a specific version of Rust.
By default, `bindgen` will target the latest stable Rust.
The `--rust-target` option accepts a specific stable version (such as "1.0" or "1.19") or "nightly".
**NOTE**: The `--unstable-rust` option is deprecated; use `--rust-target nightly` instead.
In general, most interactions with unions (either reading or writing) are unsafe, meaning you must surround union accesses in an `unsafe {}` block.
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::rust_target()`](https://docs.rs/bindgen/latest/bindgen/struct.Builder.html#method.rust_target) <!-- Update when live -->
* [`bindgen::Builder::derive_default()`](https://docs.rs/bindgen/latest/bindgen/struct.Builder.html#method.derive_default)
### Command Line
* `--rust-target`
* `--with-derive-default`
## Which union type will Bindgen generate?
Bindgen can emit one of two Rust types that correspond to C unions:
* Rust's `union` builtin (only available in Rust >= 1.19, including nightly)
* Bindgen's `BindgenUnion` (available for all Rust targets)
Bindgen uses the following logic to determine which Rust union type to emit:
* If the Rust target is >= 1.19 (including nightly) AND each field of the union can derive `Copy`, then generate a `union` builtin.
* Otherwise, generate a `BindgenUnion`.
## Using the `union` builtin
When using the `union` builtin type, there are two choices for initialization:
1. Zero
2. With a specific variant
```rust,ignore
mod bindings_builtin_union;
fn union_builtin() {
// Initialize the union to zero
let x = bindings_builtin_union::greek_t::default();
// If `--with-derive-default` option is not used, the following may be used
// to initialize the union to zero:
let x = unsafe { std::mem::zeroed::<bindings_builtin_union::greek_t>() };
// Or, it is possible to initialize exactly one variant of the enum:
let x = bindings_builtin_union::greek_t {
alfa: bindings_builtin_union::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 `BindgenUnion` type
If the target Rust version does not support the new `union` type or there is a field that cannot derive `Copy`, then bindgen will provide union-like access to a `struct`.
Interacting with these unions is slightly different than the new `union` types.
You must access union variants through a reference.
```rust,ignore
mod bindings;
fn bindgenunion() {
// `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`
```