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 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
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`