Introduction

bindgen automatically generates Rust FFI bindings to C and C++ libraries.

For example, given the C header cool.h:

typedef struct CoolStruct {
    int x;
    int y;
} CoolStruct;

void cool_function(int i, char c, CoolStruct* cs);

bindgen produces Rust FFI code allowing you to call into the cool library's functions and use its types:

#![allow(unused)]
fn main() {
/* automatically generated by rust-bindgen 0.99.9 */

#[repr(C)]
pub struct CoolStruct {
    pub x: ::std::os::raw::c_int,
    pub y: ::std::os::raw::c_int,
}

extern "C" {
    pub fn cool_function(i: ::std::os::raw::c_int,
                         c: ::std::os::raw::c_char,
                         cs: *mut CoolStruct);
}
}

Requirements

This page lists the requirements for running bindgen and how to get them.

Clang

bindgen leverages libclang to preprocess, parse, and type check C and C++ header files.

It is required to use Clang 9.0 or greater.

Installing Clang

Windows

If you use winget:

winget install LLVM.LLVM

Alternatively, you can download and install the official pre-built binary from LLVM download page.

You will also need to set LIBCLANG_PATH as an environment variable pointing to the bin directory of your LLVM install. For example, if you installed LLVM to D:\programs\LLVM, then you'd set the value to be D:\programs\LLVM\bin.

Alternatively, for Mingw64, you can install clang via

pacman -S  mingw64/mingw-w64-x86_64-clang

macOS

If you use Homebrew:

$ brew install llvm

If you use MacPorts:

$ port install clang

Debian-based Linuxes

# apt install libclang-dev

If you want to use the function bindgen::Builder::dump_preprocessed_input, then you also need the package clang.

Arch

# pacman -S clang

Fedora

# dnf install clang-devel

OpenBSD

# pkg_add llvm

Add export LIBCLANG_PATH=/usr/local/lib to your profile.

From source

If your package manager doesn't yet offer Clang 9.0, you'll need to build from source. For that, follow the instructions here.

Those instructions list optional steps. For bindgen:

  • Checkout and build clang
  • Checkout and build clang-tools-extra
  • You do not need to checkout or build compiler-rt
  • You do not need to checkout or build libcxx

Library Usage with build.rs

💡 This is the recommended way to use bindgen. 💡

Often times C and C++ headers will have platform- and architecture-specific #ifdefs that affect the shape of the Rust FFI bindings we need to create to interface Rust code with the outside world. By using bindgen as a library inside your build.rs, you can generate bindings for the current target on-the-fly. Otherwise, you would need to generate and maintain x86_64-unknown-linux-gnu-bindings.rs, x86_64-apple-darwin-bindings.rs, etc... separate bindings files for each of your supported targets, which can be a huge pain. The downside is that everyone building your crate also needs libclang available to run bindgen.

Library API Documentation

📚 There is complete API reference documentation on docs.rs 📚

Tutorial

The next section contains a detailed, step-by-step tutorial for using bindgen as a library inside build.rs.

Tutorial

The following tutorial is adapted from this blog post.

What follows is a whirlwind introductory tutorial to using bindgen from inside build.rs. We'll generate bindings to bzip2 (which is available on most systems) on-the-fly.

TL;DR? The full tutorial code is available here.

Add bindgen as a Build Dependency

First we need to declare a build-time dependency on bindgen by adding it to the [build-dependencies] section of our crate's Cargo.toml file.

Please always use the latest version of bindgen, as it has the most fixes and best compatibility. You can always check the latest version at the bindgen page in crates.io.

[build-dependencies]
bindgen = "0.71.0"

⚠️ Warning

bindgen needs to be added to the [build-dependencies] section, not the normal [dependencies] section. If you add it as a regular dependency, you will get errors like the following: error[E0463]: can't find crate for `bindgen`

Create a wrapper.h Header

The wrapper.h file will include all the various headers containing declarations of structs and functions we would like bindings for. In the particular case of bzip2, this is pretty easy since the entire public API is contained in a single header. For a project like SpiderMonkey, where the public API is split across multiple header files and grouped by functionality, we'd want to include all those headers we want to bind to in this single wrapper.h entry point for bindgen.

Here is our wrapper.h:

#include <bzlib.h>

This is also where we would add any replacement types, if we were using some.

Create a build.rs File

We create a build.rs file in our crate's root. Cargo will pick up on the existence of this file, then compile and execute it before the rest of the crate is built. This can be used to generate code at compile time. And of course in our case, we will be generating Rust FFI bindings to bzip2 at compile time. The resulting bindings will be written to $OUT_DIR/bindings.rs where $OUT_DIR is chosen by cargo and is something like ./target/debug/build/bindgen-tutorial-bzip2-sys-afc7747d7eafd720/out/.

Note that the associated shared object to bz2 is libbz2.so. In general, a lib<name>.so should be referenced in the build file by <name>.

use std::env;
use std::path::PathBuf;

fn main() {
    // Tell cargo to look for shared libraries in the specified directory
    println!("cargo:rustc-link-search=/path/to/lib");

    // Tell cargo to tell rustc to link the system bzip2
    // shared library.
    println!("cargo:rustc-link-lib=bz2");

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        // The input header we would like to generate
        // bindings for.
        .header("wrapper.h")
        // Tell cargo to invalidate the built crate whenever any of the
        // included header files changed.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

Now, when we run cargo build, our bindings to bzip2 are generated on the fly!

There's more info about build.rs files in the Cargo documentation.

Include the Generated Bindings in src/lib.rs

We can use the include! macro to dump our generated bindings right into our crate's main entry point, src/lib.rs:

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

Because bzip2's symbols do not follow Rust's style conventions, we suppress a bunch of warnings with a few #![allow(...)] pragmas.

We can run cargo build again to check that the bindings themselves compile:

$ cargo build
   Compiling bindgen-tutorial-bzip2-sys v0.1.0
    Finished debug [unoptimized + debuginfo] target(s) in 62.8 secs

And we can run cargo test to verify that the layout, size, and alignment of our generated Rust FFI structs match what bindgen thinks they should be:

$ cargo test
   Compiling bindgen-tutorial-bzip2-sys v0.1.0
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/bzip2_sys-10413fc2af207810

running 14 tests
test bindgen_test_layout___darwin_pthread_handler_rec ... ok
test bindgen_test_layout___sFILE ... ok
test bindgen_test_layout___sbuf ... ok
test bindgen_test_layout__bindgen_ty_1 ... ok
test bindgen_test_layout__bindgen_ty_2 ... ok
test bindgen_test_layout__opaque_pthread_attr_t ... ok
test bindgen_test_layout__opaque_pthread_cond_t ... ok
test bindgen_test_layout__opaque_pthread_mutex_t ... ok
test bindgen_test_layout__opaque_pthread_condattr_t ... ok
test bindgen_test_layout__opaque_pthread_mutexattr_t ... ok
test bindgen_test_layout__opaque_pthread_once_t ... ok
test bindgen_test_layout__opaque_pthread_rwlock_t ... ok
test bindgen_test_layout__opaque_pthread_rwlockattr_t ... ok
test bindgen_test_layout__opaque_pthread_t ... ok

test result: ok. 14 passed; 0 failed; 0 ignored; 0 measured

   Doc-tests bindgen-tutorial-bzip2-sys

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

Write a Sanity Test

Finally, to tie everything together, let's write a sanity test that round trips some text through compression and decompression, and then asserts that it came back out the same as it went in. This is a little wordy using the raw FFI bindings, but hopefully we wouldn't usually ask people to do this, we'd provide a nice Rust-y API on top of the raw FFI bindings for them. However, since this is for testing the bindings directly, our sanity test will use the bindings directly.

The test data I'm round tripping are some Futurama quotes I got off the internet and put in the futurama-quotes.txt file, which is read into a &'static str at compile time via the include_str!("../futurama-quotes.txt") macro invocation.

Without further ado, here is the test, which should be appended to the bottom of our src/lib.rs file:

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;
    use std::mem;

    #[test]
    fn round_trip_compression_decompression() {
        unsafe {
            let input = include_str!("../futurama-quotes.txt").as_bytes();
            let mut compressed_output: Vec<u8> = vec![0; input.len()];
            let mut decompressed_output: Vec<u8> = vec![0; input.len()];

            // Construct a compression stream.
            let mut stream: bz_stream = mem::zeroed();
            let result = BZ2_bzCompressInit(&mut stream as *mut _,
                                            1,   // 1 x 100000 block size
                                            4,   // verbosity (4 = most verbose)
                                            0);  // default work factor
            match result {
                r if r == (BZ_CONFIG_ERROR as _) => panic!("BZ_CONFIG_ERROR"),
                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
                r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"),
                r if r == (BZ_OK as _) => {},
                r => panic!("Unknown return value = {r}"),
            }

            // Compress `input` into `compressed_output`.
            stream.next_in = input.as_ptr() as *mut _;
            stream.avail_in = input.len() as _;
            stream.next_out = compressed_output.as_mut_ptr() as *mut _;
            stream.avail_out = compressed_output.len() as _;
            let result = BZ2_bzCompress(&mut stream as *mut _, BZ_FINISH as _);
            match result {
                r if r == (BZ_RUN_OK as _) => panic!("BZ_RUN_OK"),
                r if r == (BZ_FLUSH_OK as _) => panic!("BZ_FLUSH_OK"),
                r if r == (BZ_FINISH_OK as _) => panic!("BZ_FINISH_OK"),
                r if r == (BZ_SEQUENCE_ERROR as _) => panic!("BZ_SEQUENCE_ERROR"),
                r if r == (BZ_STREAM_END as _) => {},
                r => panic!("Unknown return value = {r}"),
            }

            // Finish the compression stream.
            let result = BZ2_bzCompressEnd(&mut stream as *mut _);
            match result {
                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
                r if r == (BZ_OK as _) => {},
                r => panic!("Unknown return value = {r}"),
            }

            // Construct a decompression stream.
            let mut stream: bz_stream = mem::zeroed();
            let result = BZ2_bzDecompressInit(&mut stream as *mut _,
                                              4,   // verbosity (4 = most verbose)
                                              0);  // default small factor
            match result {
                r if r == (BZ_CONFIG_ERROR as _) => panic!("BZ_CONFIG_ERROR"),
                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
                r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"),
                r if r == (BZ_OK as _) => {},
                r => panic!("Unknown return value = {r}"),
            }

            // Decompress `compressed_output` into `decompressed_output`.
            stream.next_in = compressed_output.as_ptr() as *mut _;
            stream.avail_in = compressed_output.len() as _;
            stream.next_out = decompressed_output.as_mut_ptr() as *mut _;
            stream.avail_out = decompressed_output.len() as _;
            let result = BZ2_bzDecompress(&mut stream as *mut _);
            match result {
                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
                r if r == (BZ_DATA_ERROR as _) => panic!("BZ_DATA_ERROR"),
                r if r == (BZ_DATA_ERROR_MAGIC as _) => panic!("BZ_DATA_ERROR"),
                r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"),
                r if r == (BZ_OK as _) => panic!("BZ_OK"),
                r if r == (BZ_STREAM_END as _) => {},
                r => panic!("Unknown return value = {r}"),
            }

            // Close the decompression stream.
            let result = BZ2_bzDecompressEnd(&mut stream as *mut _);
            match result {
                r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"),
                r if r == (BZ_OK as _) => {},
                r => panic!("Unknown return value = {r}"),
            }

            assert_eq!(input, &decompressed_output[..]);
        }
    }
}
}

Now let's run cargo test again and verify that everything is linking and binding properly!

$ cargo test
   Compiling bindgen-tutorial-bzip2-sys v0.1.0
    Finished debug [unoptimized + debuginfo] target(s) in 0.54 secs
     Running target/debug/deps/bindgen_tutorial_bzip2_sys-1c5626bbc4401c3a

running 15 tests
test bindgen_test_layout___darwin_pthread_handler_rec ... ok
test bindgen_test_layout___sFILE ... ok
test bindgen_test_layout___sbuf ... ok
test bindgen_test_layout__bindgen_ty_1 ... ok
test bindgen_test_layout__bindgen_ty_2 ... ok
test bindgen_test_layout__opaque_pthread_attr_t ... ok
test bindgen_test_layout__opaque_pthread_cond_t ... ok
test bindgen_test_layout__opaque_pthread_condattr_t ... ok
test bindgen_test_layout__opaque_pthread_mutex_t ... ok
test bindgen_test_layout__opaque_pthread_mutexattr_t ... ok
test bindgen_test_layout__opaque_pthread_once_t ... ok
test bindgen_test_layout__opaque_pthread_rwlock_t ... ok
test bindgen_test_layout__opaque_pthread_rwlockattr_t ... ok
test bindgen_test_layout__opaque_pthread_t ... ok
    block 1: crc = 0x47bfca17, combined CRC = 0x47bfca17, size = 2857
        bucket sorting ...
        depth      1 has   2849 unresolved strings
        depth      2 has   2702 unresolved strings
        depth      4 has   1508 unresolved strings
        depth      8 has    538 unresolved strings
        depth     16 has    148 unresolved strings
        depth     32 has      0 unresolved strings
        reconstructing block ...
      2857 in block, 2221 after MTF & 1-2 coding, 61+2 syms in use
      initial group 5, [0 .. 1], has 570 syms (25.7%)
      initial group 4, [2 .. 2], has 256 syms (11.5%)
      initial group 3, [3 .. 6], has 554 syms (24.9%)
      initial group 2, [7 .. 12], has 372 syms (16.7%)
      initial group 1, [13 .. 62], has 469 syms (21.1%)
      pass 1: size is 2743, grp uses are 13 6 15 0 11
      pass 2: size is 1216, grp uses are 13 7 15 0 10
      pass 3: size is 1214, grp uses are 13 8 14 0 10
      pass 4: size is 1213, grp uses are 13 9 13 0 10
      bytes: mapping 19, selectors 17, code lengths 79, codes 1213
    final combined CRC = 0x47bfca17

    [1: huff+mtf rt+rld {0x47bfca17, 0x47bfca17}]
    combined CRCs: stored = 0x47bfca17, computed = 0x47bfca17
test tests::round_trip_compression_decompression ... ok

test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured

   Doc-tests bindgen-tutorial-bzip2-sys

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

Publish Your Crate!

That's it! Now we can publish our crate on crates.io and we can write a nice, Rust-y API wrapping the raw FFI bindings in a safe interface. However, there is already a bzip2-sys crate providing raw FFI bindings, and there is already a bzip2 crate providing a nice, safe, Rust-y API on top of the bindings, so we have nothing left to do here!

Check out the full code on Github!

Now let's suppose we want to generate bindings for a non-system library. We will be the same crate setup as the previous tutorial. First let's create a new directory hello with two files inside it. A C source file hello.c containing

int hello() {
    return 42;
}

and a C header file hello.h containing

int hello();

Given that the library has not been compiled yet, we need to modify the build.rs build script to compile the hello.c source file into a static library:

use std::env;
use std::path::PathBuf;

use bindgen::CargoCallbacks;

fn main() {
    // This is the directory where the `c` library is located.
    let libdir_path = PathBuf::from("hello")
        // Canonicalize the path as `rustc-link-search` requires an absolute
        // path.
        .canonicalize()
        .expect("cannot canonicalize path");

    // This is the path to the `c` headers file.
    let headers_path = libdir_path.join("hello.h");
    let headers_path_str = headers_path.to_str().expect("Path is not a valid string");

    // This is the path to the intermediate object file for our library.
    let obj_path = libdir_path.join("hello.o");
    // This is the path to the static library file.
    let lib_path = libdir_path.join("libhello.a");

    // Tell cargo to look for shared libraries in the specified directory
    println!("cargo:rustc-link-search={}", libdir_path.to_str().unwrap());

    // Tell cargo to tell rustc to link our `hello` library. Cargo will
    // automatically know it must look for a `libhello.a` file.
    println!("cargo:rustc-link-lib=hello");

    // Run `clang` to compile the `hello.c` file into a `hello.o` object file.
    // Unwrap if it is not possible to spawn the process.
    if !std::process::Command::new("clang")
        .arg("-c")
        .arg("-o")
        .arg(&obj_path)
        .arg(libdir_path.join("hello.c"))
        .output()
        .expect("could not spawn `clang`")
        .status
        .success()
    {
        // Panic if the command was not successful.
        panic!("could not compile object file");
    }

    // Run `ar` to generate the `libhello.a` file from the `hello.o` file.
    // Unwrap if it is not possible to spawn the process.
    if !std::process::Command::new("ar")
        .arg("rcs")
        .arg(lib_path)
        .arg(obj_path)
        .output()
        .expect("could not spawn `ar`")
        .status
        .success()
    {
        // Panic if the command was not successful.
        panic!("could not emit library file");
    }

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        // The input header we would like to generate
        // bindings for.
        .header(headers_path_str)
        // Tell cargo to invalidate the built crate whenever any of the
        // included header files changed.
        .parse_callbacks(Box::new(CargoCallbacks::new()))
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
    bindings
        .write_to_file(out_path)
        .expect("Couldn't write bindings!");
}

Command Line Usage

Install the bindgen executable with cargo:

$ cargo install bindgen-cli

The bindgen executable is installed to ~/.cargo/bin. You have to add that directory to your $PATH to use bindgen.

bindgen takes the path to an input C or C++ header file, and optionally an output file path for the generated bindings. If the output file path is not supplied, the bindings are printed to stdout.

If we wanted to generated Rust FFI bindings from a C header named input.h and put them in the bindings.rs file, we would invoke bindgen like this:

$ bindgen input.h -o bindings.rs

For more details, pass the --help flag:

$ bindgen --help

Customizing the Generated Bindings

The translation of classes, structs, enums, and typedefs can be adjusted in a few ways:

  1. By using the bindgen::Builder's configuration methods, when using bindgen as a library.

  2. By passing extra flags and options to the bindgen executable.

  3. By adding an annotation comment to the C/C++ source code. Annotations are specially formatted HTML tags inside doxygen style comments:

    • For single line comments:

      /// <div rustbindgen></div>
      
    • For multi-line comments:

      /**
       * <div rustbindgen></div>
       */
      

We'll leave the nitty-gritty details to the docs.rs API reference and bindgen --help, but provide higher level concept documentation here.

Allowlisting

Allowlisting allows us to be precise about which type, function, and global variable definitions bindgen generates bindings for. By default, if we don't specify any allowlisting rules, everything is considered allowlisted. This may not be desirable because of either

  • the generated bindings contain a lot of extra definitions we don't plan on using, or
  • the header file contains C++ features for which Rust does not have a corresponding form (such as partial template specialization), and we would like to avoid these definitions

If we specify allowlisting rules, then bindgen will only generate bindings to types, functions, and global variables that match the allowlisting rules, or are transitively used by a definition that matches them.

Library

Command Line

  • --allowlist-type <type>
  • --allowlist-function <function>
  • --allowlist-var <var>
  • --allowlist-file <path>
  • --allowlist-item <item>

Annotations

None.

Blocklisting

If you need to provide your own custom translation of some type (for example, because you need to wrap one of its fields in an UnsafeCell), you can explicitly blocklist generation of its definition. Uses of the blocklisted type will still appear in other types' definitions. (If you don't want the type to appear in the bindings at all, make it opaque instead of blocklisting it.)

Blocklisted types are pessimistically assumed not to be able to derive any traits, which can transitively affect other types' ability to derive traits or not.

The blocklist-file option also allows the blocklisting of all items from a particular path regex, for example to block all types defined in system headers that are transitively included.

Library

Command Line

  • --blocklist-file <path>
  • --blocklist-function <function>
  • --blocklist-item <item>
  • --blocklist-type <type>
  • --blocklist-var <var>

Annotations

/// <div rustbindgen hide></div>
class Foo {
    // ...
};

Treating a Type as an Opaque Blob of Bytes

Sometimes a type definition is simply not translatable to Rust, for example it uses C++'s SFINAE for which Rust has no equivalent. In these cases, it is best to treat all occurrences of the type as an opaque blob of bytes with a size and alignment. bindgen will attempt to detect such cases and do this automatically, but other times it needs some explicit help from you.

Library

Command Line

  • --opaque-type <type>

Annotation

/// <div rustbindgen opaque></div>
class Foo {
    // ...
};

Replacing One Type with Another

The replaces annotation can be used to use a type as a replacement for other (presumably more complex) type. This is used in Stylo to generate bindings for structures that for multiple reasons are too complex for bindgen to understand.

For example, in a C++ header:

/**
 * <div rustbindgen replaces="nsTArray"></div>
 */
template<typename T>
class nsTArray_Simple {
  T* mBuffer;
public:
  // The existence of a destructor here prevents bindgen from deriving the Clone
  // trait via a simple memory copy.
  ~nsTArray_Simple() {};
};

That way, after code generation, the bindings for the nsTArray type are the ones that would be generated for nsTArray_Simple.

Replacing is only available as an annotation. To replace a C or C++ definition with a Rust definition, use blocklisting.

Preventing the Derivation of Copy and Clone

bindgen will attempt to derive the Copy and Clone traits on a best-effort basis. Sometimes, it might not understand that although adding #[derive(Copy, Clone)] to a translated type definition will compile, it still shouldn't do that for reasons it can't know. In these cases, the nocopy annotation can be used to prevent bindgen to autoderive the Copy and Clone traits for a type.

Library

Command Line

  • --no-copy <regex>

Annotations

/**
 * Although bindgen can't know, this struct is not safe to move because pthread
 * mutexes can't move in memory!
 *
 * <div rustbindgen nocopy></div>
 */
struct MyMutexWrapper {
    pthread_mutex_t raw;
    // ...
};

Preventing the Derivation of Debug

bindgen will attempt to derive the Debug traits on a best-effort basis. Sometimes, it might not understand that although adding #[derive(Debug)] to a translated type definition will compile, it still shouldn't do that for reasons it can't know. In these cases, the nodebug annotation can be used to prevent bindgen to autoderive the Debug traits for a type.

Library

Command Line

  • --no-debug <regex>

Annotations

/**
 * Although bindgen can't know, this enum is not safe to format the output.
 * the value may be combined with multiple bits in many C/C++ cases,
 * for example:
 *
 * <div rustbindgen nodebug></div>
 */
enum AVRounding {
    AV_ROUND_ZERO     = 0,
    AV_ROUND_INF      = 1,
    AV_ROUND_DOWN     = 2,
    AV_ROUND_UP       = 3,
    AV_ROUND_NEAR_INF = 5,
    AV_ROUND_PASS_MINMAX = 8192,
};

// Prototype
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding) av_const;

// Call
int64_t pts = av_rescale_rnd(40000, 3600, 90000, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);

Preventing the Derivation of Default

bindgen will attempt to derive/impl the Default traits on a best-effort basis. Sometimes, we need customize the implement of Default for certain types, In these cases, the nodefault annotation can be used to prevent bindgen to autoderive the Default traits for a type.

Library

Command Line

  • --no-default <regex>

Annotations

/**
 * We need to specify some preset values as the Default of Header.
 *
 * for example:
 *
 * <div rustbindgen nodefault></div>
 */
struct Header {
    unsigned int magic;
    unsigned char data[252];
};

...

Customize Implements

// Include the generated bindings.
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

impl Default for Header {
    fn default() -> Self {
        Self {
            magic: 0x10203040u32,
            data: [0; 252usize],
        }
    }
}

Annotating types with #[must-use]

bindgen can be instructed to annotate certain types with #[must_use].

Some libraries have a common error type, returned by lots of their functions, which needs to be checked after every call. In these cases it's useful to add #[must_use] to this type, so the Rust compiler emits a warning when the check is missing.

Library

Command Line

  • --must-use-type <regex>

Annotations

/** <div rustbindgen mustusetype></div> */
struct ErrorType {
    // ...
};

...

Making fields private

Fields can be made private for various reasons. You may wish to enforce some invariant on the fields of a structure, which cannot be achieved if the field is public and can be set by any code. For example, you may wish to ensure that a pointer always points to something appropriate.

Annotation

struct OneFieldPrivate {
    /** Null-terminated, static string. <div rustbindgen private> */
    const char *s;
    bool b;
};

/** <div rustbindgen private> */
struct MostFieldsPrivate {
    int a;
    bool b;
    /** <div rustbindgen private="false"></div> */
    char c;
};

Then in Rust:

#![allow(unused)]
fn main() {
#[repr(C)]
pub struct OneFieldPrivate {
    s: *const ::std::os::raw::c_char,
    pub b: bool,
}

impl OneFieldPrivate {
    pub fn new(s: &'static std::ffi::CStr, b: bool) -> Self {
        OneFieldPrivate { s: s.as_ptr(), b }
    }
}
}

Code Formatting

bindgen uses rustfmt to format the emitted bindings. This section describes how to adjust the rustfmt behavior when being used from bindgen.

Passing a rustfmt.toml configuration file

rustfmt should automatically use any rustfmt.toml file that is present in the directory from where bindgen will be run. If you want to use a configuration file that has a different name or that is in a different directory you can use the --rustfmt-configuration-file flag or the Builder::rustfmt_configuration_file method.

Using a nightly release of rustfmt

If the rustfmt command does not correspond to a nightly release of rustfmt but you have rustup available, you can use nightly by following these steps:

When using bindgen as a CLI application

Use rustup run to run bindgen:

$ rustup run nightly bindgen [ARGS]

When using bindgen as a library

Take the output of the following command:

$ rustup which rustfmt --toolchain=nightly

and pass it to Builder::with_rustfmt:

use bindgen::Builder;
use std::process::Command;

fn main() {
    let output = Command::new("rustup")
        .args(["which", "rustfmt", "--toolchain", "nightly"])
        .output()
        .expect("Could not spawn `rustup` command");

    assert!(
        output.status.success(),
        "Unsuccessful status code when running `rustup`: {output:?}",
    );

    let rustfmt_path =
        String::from_utf8(output.stdout).expect("The `rustfmt` path is not valid `utf-8`");

    let bindings = Builder::default()
        .header("path/to/input.h")
        .with_rustfmt(rustfmt_path)
        .generate()
        .expect("Could not generate bindings");

    bindings
        .write_to_file("path/to/output.rs")
        .expect("Could not write bindings");
}

These two methods also apply to any other toolchain available in your system.

Using prettyplease

The prettyplease crate is a minimal formatter for generated code. To format bindings using prettyplease you have to invoke bindgen with either the --formatter=prettyplease flag or the bindgen::Builder::formatter(bindgen::Formatter::Prettyplease). One of its advantages is that prettyplease can be used in minimal environments where the Rust toolchain is not installed.

How can I normalize #[doc] attributes?

bindgen emits all the documentation using #[doc] attributes by default. If you want to use the more user-friendly /// syntax, you have two options:

Use rustfmt

rustfmt can be configured to normalize documentation. To do so, you have to create a rustfmt.toml file with the following contents:

normalize_doc_attributes = true

Then, you have set up bindgen so it passes this file to rustfmt. Given that the normalize_doc_attributes option is unstable, you also have to set up bindgen to use a nightly release of rustfmt.

Use prettyplease

prettyplease normalizes documentation without any additional configuration. Then you just have to tell bindgen to use prettyplease as the code formatter.

Generating Bindings to C++

bindgen can handle some C++ features, but not all of them. To set expectations: bindgen will give you the type definitions and FFI declarations you need to build an API to the C++ library, but using those types in Rust will be nowhere near as nice as using them in C++. You will have to manually call constructors, destructors, overloaded operators, etc yourself.

When passing in header files, the file will automatically be treated as C++ if it ends in .hpp. If it doesn't, adding -x c++ clang args can be used to force C++ mode. You probably also want to use -std=c++14 or similar clang args as well.

You pretty much must use allowlisting when working with C++ to avoid pulling in all of the std::.* types, many of which bindgen cannot handle. Additionally, you may want to mark other types as opaque that bindgen stumbles on. It is recommended to mark all of std::.* opaque, and to allowlist only precisely the functions and types you intend to use.

You should read up on the FAQs as well.

Supported Features

  • Inheritance (for the most part; there are some outstanding bugs)

  • Methods

  • Bindings to constructors and destructors (but they aren't implicitly or automatically invoked)

  • Function and method overloading

  • Templates without specialization. You should be able to access individual fields of the class or struct.

Unsupported Features

When bindgen finds a type that is too difficult or impossible to translate into Rust, it will automatically treat it as an opaque blob of bytes. The philosophy is that

  1. we should always get layout, size, and alignment correct, and

  2. just because one type uses specialization, that shouldn't cause bindgen to give up on everything else.

Without further ado, here are C++ features that bindgen does not support or cannot translate into Rust:

  • Inline functions and methods: see "Why isn't bindgen generating bindings to inline functions?"

  • Template functions, methods of template classes and structs. We don't know which monomorphizations exist, and can't create new ones because we aren't a C++ compiler.

  • Anything related to template specialization:

    • Partial template specialization
    • Traits templates
    • Substitution Failure Is Not An Error (SFINAE)
  • Cross language inheritance, for example inheriting from a Rust struct in C++.

  • Automatically calling copy and/or move constructors or destructors. Supporting this isn't possible with Rust's move semantics.

  • Exceptions: if a function called through a bindgen-generated interface raises an exception that is not caught by the function itself, this will generate undefined behaviour. See the tracking issue for exceptions for more details.

  • Many C++ specific aspects of calling conventions. For example in the Itanium abi types that are "non trivial for the purposes of calls" should be passed by pointer, even if they are otherwise eligible to be passed in a register. Similarly in both the Itanium and MSVC ABIs such types are returned by "hidden parameter", much like large structs in C that would not fit into a register. This also applies to types with any base classes in the MSVC ABI (see x64 calling convention). Because bindgen does not know about these rules generated interfaces using such types are currently invalid.

Constructor semantics

bindgen will generate a wrapper for any class constructor declared in the input headers. For example, this headers file

class MyClass {
    public:
	MyClass();
        void method();
};

Will produce the following code:

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct MyClass {
    pub _address: u8,
}
extern "C" {
    #[link_name = "\u{1}_ZN7MyClass6methodEv"]
    pub fn MyClass_method(this: *mut MyClass);
}
extern "C" {
    #[link_name = "\u{1}_ZN7MyClassC1Ev"]
    pub fn MyClass_MyClass(this: *mut MyClass);
}
impl MyClass {
    #[inline]
    pub unsafe fn method(&mut self) {
        MyClass_method(self)
    }
    #[inline]
    pub unsafe fn new() -> Self {
        let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
        MyClass_MyClass(__bindgen_tmp.as_mut_ptr());
        __bindgen_tmp.assume_init()
    }
}

This MyClass::new Rust method can be used as a substitute for the MyClass C++ constructor. However, the address of the value from inside the method will be different than from the outside. This is because the __bindgen_tmp value is moved when the MyClass::new method returns.

In contrast, the C++ constructor will not move the value, meaning that the address of the value will be the same inside and outside the constructor. If the original C++ relies on this semantic difference somehow, you should use the MyClass_MyClass binding directly instead of the MyClass::new method.

In other words, the Rust equivalent for the following C++ code

MyClass instance = MyClass();
instance.method();

is not this

let instance = MyClass::new();
instance.method();

but this

let instance = std::mem::MaybeUninit::<MyClass>::uninit();
MyClass_MyClass(instance.as_mut_ptr());
instance.assume_init_mut().method();

You can easily verify this fact if you provide a implementation for MyClass and method that prints the this pointer address. However, you can ignore this fact if you know that the original C++ code does not rely on the instance address in its internal logic.

Generating Bindings to Objective-C

bindgen does not (yet) have full objective-c support but it can generate bindings for a lot of the apple frameworks without too much blocklisting.

In order to generate bindings, you will need -x objective-c as the clang args. If you'd like to use block you will need -fblocks as a clang arg as well.

Depending on your setup, you may need --generate-block to generate the block function aliases and --block-extern-crate to insert a extern crate block at the beginning of the generated bindings. The same logic applies to the --objc-extern-crate parameter.

The objective-c classes will be represented as a struct Foo(id) and a trait IFoo where Foo is the objective-c class and id is an alias for *mut objc::runtime::Object (the pointer to the objective-c instance). The trait IFoo is needed to allow for the generated inheritance.

Functions that use or return objective-c pointers of instance Foo will return Foo. The reason this works is because Foo represented as transparent. This will be helpful for a lot of objective-c frameworks however there are some cases where functions return instancetype which is a type alias for id so an occasional foo.0 may be required. An example of this would in the UIKit framework should you want to add a UILabel to a UIStackView you will need to convert the UILabel to a UIView via UIView(label.0).

Each class (struct) has an alloc and a dealloc to match that of some of the alloc methods found in NSObject.

In order to initialize a class Foo, you will have to do something like let foo = Foo(Foo::alloc().initWithStuff()).

To blocklist an Objective-C method, you should add the bindgen generated method path (e.g. IFoo::method or IFoo::class_method) as a blocklist item.

Supported Features

  • Inheritance matched to rust traits with prefixes of I which stands for interface.
  • Protocols which match to rust traits with prefixes of P which stands for Protocol.
  • Classes will generate struct Foo(id) where Foo is the class name and id is a pointer to the objective-c Object.
  • Blocks

Useful Notes

  • If you're targeting aarch64-apple-ios, you'll need to have the clang arg --target=arm64-apple-ios as mentioned here.
  • The generated bindings will almost certainly have some conflicts so you will have to blocklist a few things. There are a few cases of the parameters being poorly named in the objective-c headers. But if you're using anything with Core Foundation, you'll find that time.h as has a variable called timezone that conflicts with some of the things in NSCalendar.h.
  • Some small subset of the function headers in the apple frameworks go against apple's guidelines for parameter names and duplicate the names in the header which won't compile as mentioned here.
  • instancetype return methods does not return Self for you given class, it returns a mut * objc::runtime::Objc which is aliased as id. This is because objective-c's inheritance doesn't perfectly match that of rusts.
  • Depending on what you're trying bindgen against, you may end up including all of Core Foundation and any other frameworks. This will result in a very long compile time.

Not (yet) Supported

  • Nullability attributes which return Options.
  • Probably many other things. Feel free to open an issue.

Example crate(s)

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`

Using the Bitfield Types Generated by Bindgen

Bitfield Strategy Overview

As Rust does not support bitfields, Bindgen generates a struct for each with the following characteristics

  • Immutable getter functions for each bitfield named <bitfield>
  • Setter functions for each contiguous block of bitfields named set_<bitfield>
  • For each contiguous block of bitfields, Bindgen emits an opaque physical field that contains one or more logical bitfields
  • A static constructor new_bitfield_{1, 2, ...} with a parameter for each bitfield contained within the opaque physical field.

To keep bindgen from generating the bitfield unit struct, it can be blocklisted like any other type, i.e. --blocklist-type "__BindgenBitfieldUnit". This may be useful if you want to define a custom implementation, or your generated bindings import a pre-existing definition for the bitfield unit type.

Bitfield examples

For this discussion, we will use the following C type definitions and functions.

typedef struct {
    unsigned int a: 1;
    unsigned int b: 1;
    unsigned int c: 2;
    
} StructWithBitfields;

// Create a default bitfield
StructWithBitfields create_bitfield();

// Print a bitfield
void print_bitfield(StructWithBitfields bfield);

Bindgen creates a set of field getters and setters for interacting with the bitset. For example,

    let mut bfield = unsafe { create_bitfield() };
    
    bfield.set_a(1);
    println!("a set to {}", bfield.a());
    bfield.set_b(1);
    println!("b set to {}", bfield.b());
    bfield.set_c(3);
    println!("c set to {}", bfield.c());
    
    unsafe { print_bitfield(bfield) };

will print out

a set to 1
b set to 1
c set to 3
StructWithBitfields: a:1, b:1, c:3

Overflowing a bitfield will result in the same behavior as in C/C++: the bitfield will be set to 0.

    let mut bfield = unsafe { create_bitfield() };
    bfield.set_a(1);
    bfield.set_b(1);
    bfield.set_c(12);
    println!("c set to {} due to overflow", bfield.c());
    
    unsafe { print_bitfield(bfield) };

will print out

c set to 0 due to overflow
StructWithBitfields: a:1, b:1, c:0

To create a new bitfield in Rust, use the bitfield allocation unit constructor.

Note: This requires the Builder's derive_default to be set to true, otherwise the necessary Default functions won't be generated.

    let bfield = StructWithBitfields{
        _bitfield_1: StructWithBitfields::new_bitfield_1(0,0,0),
        ..Default::default()
    };
    
    unsafe { print_bitfield(bfield) };

This will print out

StructWithBitfields: a:0, b:0, c:0

Using C structures with Flexible Array Members

Since time immemorial, C programmers have been using what was called "the struct hack". This is a technique for packing a fixed-size structure and a variable-sized tail within the same memory allocation. Typically this looks like:

struct MyRecord {
    time_t timestamp;
    unsigned seq;
    size_t len;
    char payload[0];
};

Because this is so useful, it was standardized in C99 as "flexible array members", using almost identical syntax:

struct MyRecord {
    time_t timestamp;
    unsigned seq;
    size_t len;
    char payload[]; // NOTE: empty []
};

Bindgen supports these structures in two different ways.

__IncompleteArrayField

By default, bindgen will generate the corresponding Rust structure:

#[repr(C)]
struct MyRecord {
    pub timestamp: time_t,
    pub seq: ::std::os::raw::c_uint,
    pub len: usize,
    pub payload: __IncompleteArrayField<::std::os::raw::c_char>,
}

The __IncompleteArrayField type is zero-sized, so this structure represents the prefix without any trailing data. In order to access that data, it provides the as_slice unsafe method:

    // SAFETY: there's at least `len` bytes allocated and initialized after `myrecord`
    let payload = unsafe { myrecord.payload.as_slice(myrecord.len) };

There's also as_mut_slice which does the obvious.

These are unsafe simply because it's up to you to provide the right length (in elements of whatever type payload is) as there's no way for Rust or Bindgen to know. In this example, the length is a very straightforward len field in the structure, but it could be encoded in any number of ways within the structure, or come from somewhere else entirely.

One big caveat with this technique is that std::mem::size_of (or size_of_val) will only include the size of the prefix structure. if you're working out how much storage the whole structure is using, you'll need to add the suffix yourself.

Using Dynamically Sized Types

If you invoke bindgen with the --flexarray-dst option, it will generate something not quite like this:

#[repr(C)]
struct MyRecord {
    pub timestamp: time_t,
    pub seq: ::std::os::raw::c_uint,
    pub len: usize,
    pub payload: [::std::os::raw::c_char],
}

Rust has a set of types which are almost exact analogs for these Flexible Array Member types: the Dynamically Sized Type ("DST").

This looks almost identical to a normal Rust structure, except that you'll note the type of the payload field is a raw slice [...] rather than the usual reference to slice &[...].

That payload: [c_char] is telling Rust that it can't directly know the total size of this structure - the payload field takes an amount of space that's determined at runtime. This means you can't directly use values of this type, only references: &MyRecord.

In practice, this is very awkward. So instead, bindgen generates:

#[repr(C)]
struct MyRecord<FAM: ?Sized = [::std::os::raw::c_char; 0]> {
    pub timestamp: time_t,
    pub seq: ::std::os::raw::c_uint,
    pub len: usize,
    pub payload: FAM,
}

That is:

  1. a type parameter FAM which represents the type of the payload field,
  2. it's ?Sized meaning it can be unsized (ie, a DST)
  3. it has the default type of [c_char; 0] - that is a zero-sized array of characters

This means that referencing plain MyRecord will be exactly like MyRecord with __IncompleteArrayField: it is a fixed-sized structure which you can manipulate like a normal Rust value.

But how do you get to the DST part?

Bindgen will also implement a set of helper methods for this:

// Static sized variant
impl MyRecord<[::std::os::raw::c_char; 0]> {
    pub unsafe fn flex_ref(&self, len: usize) -> &MyRecord<[::std::os::raw::c_char]> { ... }
    pub unsafe fn flex_mut_ref(&mut self, len: usize) -> &mut MyRecord<[::std::os::raw::c_char]> { ... }
    // And some raw pointer variants
}

These will take a sized MyRecord<[c_char; 0]> and a length in elements, and return a reference to a DST MyRecord<[c_char]> where the payload field is a fully usable slice of len characters.

The magic here is that the reference is a fat pointer, which not only encodes the address, but also the dynamic size of the final field, just like a reference to a slice is. This means that you get full bounds checked access to the payload field like any other Rust slice.

It also means that doing mem::size_of_val(myrecord) will return the complete size of this structure, including the suffix.

You can go the other way:

// Dynamic sized variant
impl MyRecord<[::std::os::raw::c_char]> {
    pub fn fixed(&self) -> (&MyRecord<[::std::os::raw::c_char; 0]>, usize) { ... }
    pub fn fixed_mut(&mut self) -> (&mut MyRecord<[::std::os::raw::c_char; 0]>, usize) { ... }
    pub fn layout(len: usize) -> std::alloc::Layout { ... }
}

which takes the DST variant of the structure and returns the sized variant, along with the number of elements are after it. These are all completely safe because all the information needed is part of the fat &self reference.

The layout function takes a length and returns the Layout - that is, size and alignment, so that you can allocate memory for the structure (for example, using malloc so you can pass it to a C function).

Unfortunately the language features needed to support these methods are still unstable:

As a result, if you don't specify --rust-target nightly you'll just get the bare type definitions, but no real way to use them. It's often convenient to add the

--raw-line '#![feature(ptr_metadata,layout_for_ptr)]'

option if you're generating Rust as a stand-alone crate. Otherwise you'll need to add the feature line to your containing crate.

Frequently Asked Questions

Why isn't bindgen generating methods for this allowlisted class?

Are the methods inline methods, or defined inline in the class? For example:

class Dooder {
  public:
    // Function defined inline in the class.
    int example_one() { return 1; }

    // `inline` function whose definition is supplied later in the header, or in
    // another header.
    inline bool example_two();
};

inline bool Dooder::example_two() {
    return true;
}

If so, see "Why isn't bindgen generating bindings to inline functions?"

If not, consider filing an issue!

Why isn't bindgen generating bindings to inline functions?

These functions don't typically end up in object files or shared libraries with symbols that we can reliably link to, since they are instead inlined into each of their call sites. Therefore, we don't generate bindings to them, since that creates linking errors.

However, if you are compiling the C/C++ yourself (rather than using a system shared library, for example), then you can pass -fkeep-inline-functions or -fno-inline-functions to gcc or clang, and invoke bindgen with either the bindgen::Builder::generate_inline_functions method or the --generate-inline-functions flag.

Note that these functions and methods are usually marked inline for a reason: they tend to be hot. The above workaround makes them an out-of-line call, which might not provide acceptable performance.

As an alternative, you can invoke bindgen with either the bindgen::Builder::wrap_static_fns method or the --wrap-static-fns flag. Which generates a C source file that can be compiled against the input headers to produce Rust headers for static and static inline functions. See How to handle static inline functions for further information.

Does bindgen support the C++ Standard Template Library (STL)?

Sort of. A little. Depends what you mean by "support".

Most functions, methods, constructors, and destructors are inline in the STL. That ties our hands when it comes to linking: "Why isn't bindgen generating bindings to inline functions?"

As far as generating opaque blobs of bytes with the correct size and alignment, bindgen can do pretty well. This is typically enough to let you use types that transitively contain STL things. We generally recommend marking std::.* as opaque, and then allowlisting only the specific things you need from the library you're binding to that is pulling in STL headers.

How to deal with bindgen generated padding fields?

Depending the architecture, toolchain versions and source struct, it is possible that bindgen will generate padding fields named __bindgen_padding_N. As these fields might be present when compiling for one architecture but not for an other, you should not initialize these fields manually when initializing the struct. Instead, use the Default trait. You can either enable this when constructing the Builder using the derive_default method, or you can implement this per struct using:

impl Default for SRC_DATA {
    fn default() -> Self {
        unsafe { std::mem::zeroed() }
    }
}

This makes it possible to initialize SRC_DATA by:

SRC_DATA {
    field_a: "foo",
    field_b: "bar",
    ..Default::default()
}

In the case bindgen generates a padding field, then this field will be automatically initialized by ..Default::default().

How to generate bindings for a custom target?

To generate bindings for a custom target you only need to pass the --target argument to libclang. For example, if you want to generate bindings for the armv7a-none-eabi target using the command line, you need to invoke bindgen like so:

$ bindgen <input_headers> -- --target=armv7a-none-eabi

If you are using bindgen as a library, you should call builder.clang_arg("--target=armv7a-none-eabi") on your builder.

Why isn't bindgen generating documentation for system headers?

By default, Bindgen does not generate documentation for system headers because libclang does not provide this information. To address this, you should call builder.clang_arg("-fretain-comments-from-system-headers") on your builder.