-
Notifications
You must be signed in to change notification settings - Fork 707
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #737 - jamesmunns:using-unions, r=fitzgen
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
Showing
2 changed files
with
133 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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` | ||
``` |