Library / SDK
mozilla/cbindgen avatar
mozilla/cbindgen

cbindgen: generating C and C++ headers from a Rust crate

A project for generating C bindings from Rust code

2,956 stars387 forksRustMPL-2.0

At a glance

What is it?
cbindgen reads a Rust crate with a public C API and emits C or C++11 headers from it. It is a one-way generator, not a binding importer, and its own README admits the feature set grew ad hoc around the maintainers' use cases.
Who is it for?
Adopt cbindgen when you own the Rust side of an FFI boundary and want the header regenerated from the source rather than hand-edited: it installs with cargo install --force cbindgen or brew install cbindgen, and a run needs a config, a crate name and an output path. Skip it if you need to go the other direction, from an existing C header into Rust, which is bindgen's job, or if you want a shared C++ type system across the boundary, which cxx is built around.
Can I use it commercially?
Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository last received commits 38 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem cbindgen solves, and who actually has it

A Rust library that exposes a C API needs a header file. Someone writing C, C++ or another language with a C FFI needs declarations for the exported functions, the structs they pass, and the enum constants they compare against. Writing that header by hand means keeping two descriptions of the same interface in sync, and the header drifts the moment a field is added to a struct.

cbindgen inverts that. The Rust source is the single description, and the header is a build artifact derived from it. The README frames the trade-off plainly: doing it by hand "is also much more likely to be error-prone than machine-generated headers that are based on your actual Rust code." The claim behind that is that the cbindgen developers worked with the Rust developers so the generated headers reflect actual guarantees about Rust's type layout and ABI.

The audience is narrow and specific. You are writing Rust that other languages call into, not Rust that calls out. The README lists projects using it in production, among them webrender and stylo inside mozilla-central, milksnake, maturin, tquic and metatensor. Those are all cases where a Rust core is consumed from C, C++ or a language binding layer. If your crate is a normal Rust library consumed by other Rust code, cbindgen has nothing to do.

How cbindgen reads a crate and what it emits

The mechanism is source analysis, not compilation against a linked artifact. cbindgen depends on syn, quote and proc-macro2, which are the standard crates for parsing and manipulating Rust syntax trees. It parses the crate's Rust source, builds its own internal representation of the exported items, and prints that representation as C or C++11.

That has a direct consequence for what you get. Because the input is syntax rather than a compiled rlib, cbindgen can generate a header for a crate it cannot build, and it does not need the crate's dependencies resolved to emit declarations. It also means the quality of the output depends on how well cbindgen's parser understands the constructs you used, which is exactly the weak point the README concedes.

The output language is a switch, not a separate tool. The README states that running cbindgen without a language flag produces a header for C++, and that for C you add the --lang c switch. The two outputs are not cosmetic variants. The README argues C++ headers can use operator overloads, constructors, enum classes and templates to make the API more Rust-like, while C headers are what you emit when you want confidence that whoever you are interoperating with can handle them. The same Rust library can emit both.

There is a library form as well. cbindgen exposes a lib target alongside the binary, so it can be driven from a build.rs. The README notes there is not much practical difference between the two, since cbindgen is a simple Rust library with no interesting dependencies, and lays out the cost of each: using the standalone program means people building your software need it installed, while using it as a library means people may have to build cbindgen more frequently, for example every time they update their Rust compiler.

Installing cbindgen and generating a first header

The README gives two install paths. The cargo one is the primary route and the --force flag exists to update an existing installation to the latest version.

bash
cargo install --force cbindgen

Homebrew is the alternative on macOS or Linux where Homebrew is present.

bash
brew install cbindgen

To run it you need two things: a configuration file, which the README says can be empty to start, and a Rust crate with a public C API. A template configuration is provided in the repository as template.toml if you would rather start from a populated one. The command takes the config path, the crate name and the output path.

bash
cbindgen --config cbindgen.toml --crate my_rust_library --output my_header.h

That produces a header for C++. For C, add the --lang c switch to the same command. After the run you should have my_header.h next to wherever you pointed the output, containing declarations derived from the crate's public C API. The README points at cbindgen --help for the remaining options rather than enumerating them, so treat the three flags above as the starting shape and read --help for the rest.

If you want the header regenerated as part of the build instead of by hand, the library form is the route, and the README's own framing is the deciding factor: a standalone binary is a tool your downstream builders must install, whereas a build.rs dependency is compiled by them. The repository also carries tests/rust/ as the working set of examples, which the README describes as containing plenty of interesting examples of the features, since there is no tailored example application.

The ad hoc development model is the real limitation

The README states it directly: development of cbindgen has been largely adhoc, as features were added to support the use cases of the maintainers, and as a result cbindgen may randomly fail to support some particular situation simply because no one has put in the effort to handle it yet. It asks users to file an issue, and adds that since the maintainers have other jobs, the reporter may need to do the implementation work too.

That is an unusual thing for a project to say about itself, and it should shape how you evaluate it. The failure mode is not a crash on a documented path. It is a construct in your crate that the tool does not model, discovered when the generated header is wrong or missing a declaration. The tests directory is therefore the closest thing to a coverage map: tests/rust/ holds the cases that are handled, and the README offers it as the example set.

A second constraint is the toolchain floor. The README badge states Rust 1.70 or newer, while Cargo.toml sets rust-version to 1.74. Those two numbers disagree, and the manifest is the one the build enforces, so plan for 1.74 from the published crate. The repository also pins a toolchain through rust-toolchain.toml, which affects building cbindgen from source but not consuming it.

There is also a release cadence constraint. The README says cbindgen does not have a fixed release calendar and asks users to file an issue requesting a release if something fixed on trunk is needed in a published version. If your build depends on a fix that has landed but not shipped, the published crate will not have it, and the README's suggested path is to ask, not to wait on a schedule.

cbindgen versus bindgen and cxx, and the direction that matters

The most common confusion around cbindgen is that it is the mirror image of bindgen, and the direction is the whole point. cbindgen takes Rust and produces C or C++ headers. It does not read a C header and produce Rust. If you have an existing C library and want to call it from Rust, cbindgen is the wrong tool and will not help you; you need the opposite direction of translation. The README never claims otherwise, and its own description is one sentence long: it creates C/C++11 headers for Rust libraries which expose a public C API.

cxx differs in kind rather than direction. cbindgen generates a header and leaves the boundary to you, which means the two sides are connected by whatever ABI and calling convention you agree on, expressed as plain C declarations. cxx is a bridge: it defines a shared vocabulary of types that both languages understand, with the Rust and C++ sides generated to agree on it. The practical difference shows up when you need to pass something richer than a struct of primitives. With cbindgen you are writing the C++ side against the generated declarations and accepting a C-shaped interface. With cxx you are working inside a type system that spans both languages.

Choosing between them is not about maturity. It is about whether you want a header file that a C consumer can read and use without any cxx runtime on the other side, or a two-sided bridge. cbindgen's output is a plain header, which is what makes it usable from languages that only speak C.

For the build side, the related search terms point at Corrosion and general Rust FFI crates, which address adjacent problems: wiring a Rust crate into a CMake build, or providing FFI helpers. Neither replaces cbindgen's job of emitting the declarations themselves.

Maintenance, licensing and the upgrade cost

The repository is not archived, and the most recent push recorded is 2026-08-22. Releases are irregular but present: 0.29.4 and 0.29.3 both on 2026-06-10, and 0.29.2 on 2026-04-01. The README's statement about no fixed release calendar is consistent with that spacing.

For upgrade cost, the thing to watch is not the cbindgen version but the header diff. Because the header is a generated artifact, an upgrade that changes how a type is emitted will show up as a change in a file you may be committing. The repository ships a CHANGES file at the top level, which is where the release-to-release record lives, and the docs.md user documentation is the reference for configuration behavior. Neither the README nor the manifest documents a rollback procedure for a bad generation, so the practical rollback is pinning the cbindgen version in your build and regenerating.

On licensing, cbindgen is MPL-2.0, stated in the Cargo.toml manifest and present as a LICENSE file at the repository root. MPL-2.0 is a file-level copyleft license. The question that matters for most users is what it means for generated output rather than for the tool itself, and the repository material does not address that question. If your headers are distributed under terms you have already chosen, get your own answer on the generated-output question rather than assuming either way. This is not legal advice.

Editorial conclusion

Adopt cbindgen when you own the Rust side of an FFI boundary and want the header regenerated from the source rather than hand-edited: it installs with cargo install --force cbindgen or brew install cbindgen, and a run needs a config, a crate name and an output path. Skip it if you need to go the other direction, from an existing C header into Rust, which is bindgen's job, or if you want a shared C++ type system across the boundary, which cxx is built around. Before depending on it, verify that the specific constructs in your crate are covered by the tests under tests/rust/, because the README states the development has been largely adhoc and some situations are simply unsupported until someone implements them.

Frequently asked questions

How do I install cbindgen?

The README gives two options: cargo install --force cbindgen, where --force updates an existing installation to the latest version, or brew install cbindgen with Homebrew.

How do I use cbindgen?

You need a configuration file such as cbindgen.toml, which can be empty to start, and a Rust crate with a public C API. Then run cbindgen --config cbindgen.toml --crate my_rust_library --output my_header.h, adding --lang c if you want C instead of C++.

What is the difference between cbindgen and bindgen?

They run in opposite directions. cbindgen creates C/C++11 headers for Rust libraries that expose a public C API, so it goes from Rust to a header. The README does not describe reading an existing C header and producing Rust, which is the other direction.

How does cbindgen compare with cxx?

cbindgen emits a header and leaves the boundary to you, with C++ output able to use operator overloads, constructors, enum classes and templates. cxx is not described in the cbindgen README, so the repository material does not offer a direct comparison.

Official sources

  1. Issues
  2. License: MPL-2.0
  3. mozilla/cbindgen on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mozilla-cbindgen.svg)](https://hysenlabs.com/projects/mozilla-cbindgen)