Skip to content

Update doc for unions #922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 36 additions & 30 deletions book/src/using-unions.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# 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.
**NOTE**: Rust 1.19 stabilized the `union` type (see Rust issue [#32836](https://github.com/rust-lang/rust/issues/32836)).

By using the flag `--unstable-rust`, bindgen will generate the preliminary unstable `union` type.
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".

In general, most interactions with unions (either reading or writing) are unsafe.
**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:

Expand All @@ -31,46 +35,47 @@ typedef union {

### Library

* [`bindgen::Builder::unstable_rust()`](https://docs.rs/bindgen/0.25.3/bindgen/struct.Builder.html#method.unstable_rust)
* [`bindgen::Builder::derive_default()`](https://docs.rs/bindgen/0.25.3/bindgen/struct.Builder.html#method.derive_default)
* [`bindgen::Builder::rust_target()`](https://docs.rs/bindgen/0.29.0/bindgen/struct.Builder.html#method.rust_target) <!-- Update when live -->
* [`bindgen::Builder::derive_default()`](https://docs.rs/bindgen/0.29.0/bindgen/struct.Builder.html#method.derive_default)

### Command Line

* `--unstable-rust`
* `--rust-target`
* `--with-derive-default`

## Using the unstable `union` version
## Which union type will Bindgen generate?

With `struct`s generated by bindgen from C, it is possible to initialize fields in a "normal" rust way:
Bindgen can emit one of two Rust types that correspond to C unions:

```rust,ignore
mod bindings;
* Rust's `union` builtin (only available in Rust >= 1.19, including nightly)
* Bindgen's `BindgenUnion` (available for all Rust targets)

fn main() {
let x = bindings::alpha_t {
a: 1,
b: -1,
};
}
```
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:

When using the unstable `union` type, there are two choices for initialization: Zeroed, and with a specific variant.
1. Zero
2. With a specific variant

```rust,ignore
#![feature(untagged_unions)]
mod bindings_unstable;
mod bindings_builtin_union;

fn unstable() {
fn union_builtin() {
// Initalize the union to zero
let x = bindings_unstable::greek_t::default();
let x = bindings_builtin_union::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>() };
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_unstable::greek_t {
alfa: bindings_unstable::alpha_t {
let x = bindings_builtin_union::greek_t {
alfa: bindings_builtin_union::alpha_t {
a: 1,
b: -1,
},
Expand All @@ -83,16 +88,17 @@ fn unstable() {
}
```

## Using the stable BindgenUnion types
## Using the `BindgenUnion` type

For versions of Rust that do not support the new `union` type, bindgen will generate types which provide union-like access to structure fields.
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. Whenever a variant of the union is accessed, it must be done through a reference.
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 stable() {
fn bindgenunion() {
// `default()` or `zeroed()` may still be used with Bindgen's Union types
let mut x = bindings::greek_t::default();

Expand All @@ -116,7 +122,7 @@ fn stable() {
}
```

If you attempt to access a BindgenUnion field directly, you will see errors like this:
If you attempt to access a `BindgenUnion` field directly, you will see errors like this:

```text
error[E0308]: mismatched types
Expand Down
30 changes: 18 additions & 12 deletions src/options.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@

use bindgen::{Builder, CodegenConfig, RUST_TARGET_STRINGS, RustTarget, builder};
use clap::{App, Arg};
use std::fs::File;
use std::io::{self, Error, ErrorKind, Write, stderr};
use std::str::FromStr;
use std::path::PathBuf;
use std::str::FromStr;

/// Construct a new [`Builder`](./struct.Builder.html) from command line flags.
pub fn builder_from_flags<I>(
Expand All @@ -14,8 +13,10 @@ where
I: Iterator<Item = String>,
{
let rust_target_help = format!(
"Version of the Rust compiler to target. Valid options are: {:?}.",
RUST_TARGET_STRINGS);
"Version of the Rust compiler to target. Valid options are: {:?}. Defaults to {:?}.",
RUST_TARGET_STRINGS,
String::from(RustTarget::default())
);

let matches = App::new("bindgen")
.version(env!("CARGO_PKG_VERSION"))
Expand Down Expand Up @@ -262,8 +263,10 @@ where

if matches.is_present("unstable-rust") {
builder = builder.rust_target(RustTarget::Nightly);
writeln!(&mut stderr(), "warning: the `--unstable-rust` option is deprecated")
.expect("Unable to write error message");
writeln!(
&mut stderr(),
"warning: the `--unstable-rust` option is deprecated"
).expect("Unable to write error message");
}

if let Some(rust_target) = matches.value_of("rust-target") {
Expand Down Expand Up @@ -487,18 +490,21 @@ where
let path = PathBuf::from(path_str);

if !path.is_absolute() {
return Err(Error::new(ErrorKind::Other,
"--rustfmt-configuration--file needs to be an absolute path!"));
return Err(Error::new(
ErrorKind::Other,
"--rustfmt-configuration--file needs to be an absolute path!",
));
}

if path.to_str().is_none() {
return Err(
Error::new(ErrorKind::Other,
"--rustfmt-configuration-file contains non-valid UTF8 characters."));
return Err(Error::new(
ErrorKind::Other,
"--rustfmt-configuration-file contains non-valid UTF8 characters.",
));
}

builder = builder.rustfmt_configuration_file(Some(path));
}
}

let verbose = matches.is_present("verbose");

Expand Down