Using the Union Types Generated by Bindgen
NOTE: Rust 1.19 stabilized the union
type (see Rust issue #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:
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
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 aunion
builtin. - Otherwise, generate a
BindgenUnion
.
Using the union
builtin
When using the union
builtin type, there are two choices for initialization:
- Zero
- With a specific variant
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.
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:
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`