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
#ifdef
s 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:
-
By using the
bindgen::Builder
's configuration methods, when usingbindgen
as a library. -
By passing extra flags and options to the
bindgen
executable. -
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
bindgen::Builder::allowlist_type
bindgen::Builder::allowlist_function
bindgen::Builder::allowlist_var
bindgen::Builder::allowlist_file
bindgen::Builder::allowlist_item
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
bindgen::Builder::blocklist_file
bindgen::Builder::blocklist_function
bindgen::Builder::blocklist_item
bindgen::Builder::blocklist_type
bindgen::Builder::blocklist_var
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
-
we should always get layout, size, and alignment correct, and
-
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)
whereFoo
is the class name andid
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 inNSCalendar.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 amut * objc::runtime::Objc
which is aliased asid
. 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
Option
s. - 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 aunion
builtin. - Otherwise, generate a
BindgenUnion
.
Using the union
builtin
When using the union
builtin type, there are two choices for initialization:
- Zero
- 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:
- a type parameter
FAM
which represents the type of thepayload
field, - it's
?Sized
meaning it can be unsized (ie, a DST) - 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:
- ptr_metadata, which enables all the fixed<->DST conversions, and
- layout_for_ptr,
which allows he
layout
method
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? - Why isn't
bindgen
generating bindings to inline functions? - Does
bindgen
support the C++ Standard Template Library (STL)? - How to deal with bindgen generated padding fields?
- How to generate bindings for a custom target?
- Why isn't
bindgen
generating documentation for system headers?
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
.