diff options
-rw-r--r-- | book/src/SUMMARY.md | 1 | ||||
-rw-r--r-- | book/src/using-unions.md | 132 |
2 files changed, 133 insertions, 0 deletions
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 1a1818f8..43d227c0 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -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) diff --git a/book/src/using-unions.md b/book/src/using-unions.md new file mode 100644 index 00000000..66ba6d13 --- /dev/null +++ b/book/src/using-unions.md @@ -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` +```
\ No newline at end of file |